diff --git a/apps/loopover-ui/public/openapi.json b/apps/loopover-ui/public/openapi.json index 4a8bfd6ef3..d481ba8a87 100644 --- a/apps/loopover-ui/public/openapi.json +++ b/apps/loopover-ui/public/openapi.json @@ -14584,6 +14584,25 @@ "recommendation", "summary" ] + }, + "AmsNotificationsAccepted": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "accepted": { + "type": "number" + }, + "enqueued": { + "type": "number" + } + }, + "required": [ + "login", + "accepted", + "enqueued" + ] } }, "parameters": {}, @@ -19004,6 +19023,108 @@ } ] } + }, + "/v1/contributors/{login}/ams-notifications": { + "post": { + "summary": "Enqueue AMS notification events for a contributor", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "login", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "events": { + "type": "array", + "items": { + "type": "object", + "properties": { + "eventType": { + "type": "string", + "enum": [ + "ams_attempt_started", + "ams_attempt_failed", + "ams_governor_paused", + "ams_pr_outcome" + ] + }, + "repoFullName": { + "type": "string" + }, + "pullNumber": { + "type": "integer", + "minimum": 0 + }, + "dedupKey": { + "type": "string" + }, + "deeplink": { + "type": "string" + }, + "actorLogin": { + "type": "string" + }, + "detectedAt": { + "type": "string" + } + }, + "required": [ + "eventType", + "repoFullName", + "pullNumber", + "dedupKey", + "deeplink", + "actorLogin", + "detectedAt" + ] + }, + "minItems": 1, + "maxItems": 20 + } + }, + "required": [ + "events" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Accepts AMS-relevant notification events (attempt start/fail, governor pause, PR outcome) and evaluates them through the existing notify-evaluate → notify-deliver path (#7657).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AmsNotificationsAccepted" + } + } + } + }, + "400": { + "description": "Invalid AMS notification body" + }, + "403": { + "description": "Forbidden when login does not match the authenticated session" + } + }, + "security": [ + { + "LoopOverBearer": [] + }, + { + "LoopOverSessionCookie": [] + } + ] + } } }, "servers": [ diff --git a/packages/loopover-miner/lib/ams-notifications.d.ts b/packages/loopover-miner/lib/ams-notifications.d.ts new file mode 100644 index 0000000000..d11a79add3 --- /dev/null +++ b/packages/loopover-miner/lib/ams-notifications.d.ts @@ -0,0 +1,65 @@ +export type AmsNotificationEventPayload = { + eventType: "ams_attempt_started" | "ams_attempt_failed" | "ams_governor_paused" | "ams_pr_outcome"; + recipientLogin: string; + repoFullName: string; + pullNumber: number; + dedupKey: string; + deeplink: string; + actorLogin: string; + detectedAt: string; +}; +export type AmsNotificationPublishResult = { + sent: number; + error?: string; +}; +export type AmsNotificationFetch = (url: string, init?: { + method?: string; + headers?: Record; + body?: string; + signal?: AbortSignal; +}) => Promise; +export type PublishAmsNotificationEventsOptions = { + env?: Record; + fetchFn?: AmsNotificationFetch; + timeoutMs?: number; + /** Test/self-host inject: mirrors job-dispatch evaluate → notify-deliver without HTTP. */ + dispatch?: (events: AmsNotificationEventPayload[]) => Promise; +}; +export declare const DEFAULT_AMS_NOTIFICATION_TIMEOUT_MS = 10000; +export declare function buildAmsAttemptStartedPayload(input: { + recipientLogin: string; + repoFullName: string; + issueNumber: number; + attemptId: string; + detectedAt?: string; +}): AmsNotificationEventPayload; +export declare function buildAmsAttemptFailedPayload(input: { + recipientLogin: string; + repoFullName: string; + issueNumber: number; + attemptId: string; + reason?: string | null; + detectedAt?: string; +}): AmsNotificationEventPayload; +export declare function buildAmsGovernorPausedPayload(input: { + recipientLogin: string; + reason?: string | null; + pausedAt?: string; + detectedAt?: string; +}): AmsNotificationEventPayload; +export declare function buildAmsPrOutcomePayload(input: { + recipientLogin: string; + repoFullName: string; + pullNumber: number; + decision: "merged" | "closed"; + closedAt?: string | null; + detectedAt?: string; +}): AmsNotificationEventPayload; +/** + * Publish AMS notification events through the hosted evaluate → notify-deliver path. Prefer an injected + * `dispatch` (tests / in-process self-host). Otherwise POST to `/v1/contributors/:login/ams-notifications` + * when a loopover-mcp session is on disk. Never throws. + */ +export declare function publishAmsNotificationEvents(events: AmsNotificationEventPayload[], options?: PublishAmsNotificationEventsOptions): Promise; +/** Fire-and-forget wrapper for sync call sites (never awaits into the caller's critical path). */ +export declare function scheduleAmsNotificationEvents(events: AmsNotificationEventPayload[], options?: PublishAmsNotificationEventsOptions): void; diff --git a/packages/loopover-miner/lib/ams-notifications.js b/packages/loopover-miner/lib/ams-notifications.js new file mode 100644 index 0000000000..6db68594d0 --- /dev/null +++ b/packages/loopover-miner/lib/ams-notifications.js @@ -0,0 +1,146 @@ +// AMS → hosted badge notifications (#7657). Builds DetectedNotificationEvent-shaped AMS kinds and POSTs them +// to the contributor ams-notifications ingest, which evaluates through evaluateNotificationEvent → +// notify-deliver (same handoff as src/queue/job-dispatch.ts). Fail-soft: a missing session or network blip +// never breaks the miner's real work. No parallel local notification store. +import { resolveLoopoverBackendSession } from "./github-token-resolution.js"; +export const DEFAULT_AMS_NOTIFICATION_TIMEOUT_MS = 10_000; +function normalizeLogin(login) { + return login.trim().toLowerCase(); +} +function nowIso() { + return new Date().toISOString(); +} +function githubIssueDeeplink(repoFullName, issueNumber) { + return `https://github.com/${repoFullName}/issues/${issueNumber}`; +} +function githubPullDeeplink(repoFullName, pullNumber) { + return `https://github.com/${repoFullName}/pull/${pullNumber}`; +} +export function buildAmsAttemptStartedPayload(input) { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + return { + eventType: "ams_attempt_started", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.issueNumber, + dedupKey: `ams_attempt_started:${input.repoFullName}#${input.issueNumber}:${input.attemptId}`, + deeplink: githubIssueDeeplink(input.repoFullName, input.issueNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} +export function buildAmsAttemptFailedPayload(input) { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const reasonKey = input.reason?.trim() ? `:${input.reason.trim().slice(0, 80)}` : ""; + return { + eventType: "ams_attempt_failed", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.issueNumber, + dedupKey: `ams_attempt_failed:${input.repoFullName}#${input.issueNumber}:${input.attemptId}${reasonKey}`, + deeplink: githubIssueDeeplink(input.repoFullName, input.issueNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} +export function buildAmsGovernorPausedPayload(input) { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const pausedAt = input.pausedAt ?? detectedAt; + const reasonKey = input.reason?.trim() ? `:${input.reason.trim().slice(0, 80)}` : ""; + return { + eventType: "ams_governor_paused", + recipientLogin, + repoFullName: "ams/governor", + pullNumber: 0, + dedupKey: `ams_governor_paused:${recipientLogin}:${pausedAt}${reasonKey}`, + deeplink: "https://github.com/JSONbored/loopover", + actorLogin: recipientLogin, + detectedAt, + }; +} +export function buildAmsPrOutcomePayload(input) { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const closedAt = input.closedAt?.trim() || detectedAt; + return { + eventType: "ams_pr_outcome", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.pullNumber, + dedupKey: `ams_pr_outcome:${input.repoFullName}#${input.pullNumber}:${input.decision}:${closedAt}`, + deeplink: githubPullDeeplink(input.repoFullName, input.pullNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} +/** + * Publish AMS notification events through the hosted evaluate → notify-deliver path. Prefer an injected + * `dispatch` (tests / in-process self-host). Otherwise POST to `/v1/contributors/:login/ams-notifications` + * when a loopover-mcp session is on disk. Never throws. + */ +export async function publishAmsNotificationEvents(events, options = {}) { + if (!Array.isArray(events) || events.length === 0) + return { sent: 0 }; + if (options.dispatch) { + try { + await options.dispatch(events); + return { sent: events.length }; + } + catch (error) { + return { sent: 0, error: error instanceof Error ? error.message.slice(0, 160) : "dispatch_failed" }; + } + } + const env = options.env ?? process.env; + const session = resolveLoopoverBackendSession(env); + if (!session) + return { sent: 0, error: "no_session" }; + const recipientLogin = normalizeLogin(events[0].recipientLogin); + if (!recipientLogin) + return { sent: 0, error: "missing_recipient" }; + if (events.some((event) => normalizeLogin(event.recipientLogin) !== recipientLogin)) { + return { sent: 0, error: "mixed_recipients" }; + } + const fetchFn = options.fetchFn ?? fetch; + const timeoutMs = options.timeoutMs ?? DEFAULT_AMS_NOTIFICATION_TIMEOUT_MS; + const url = `${session.apiUrl}/v1/contributors/${encodeURIComponent(recipientLogin)}/ams-notifications`; + const body = JSON.stringify({ + events: events.map(({ eventType, repoFullName, pullNumber, dedupKey, deeplink, actorLogin, detectedAt }) => ({ + eventType, + repoFullName, + pullNumber, + dedupKey, + deeplink, + actorLogin, + detectedAt, + })), + }); + try { + const response = await fetchFn(url, { + method: "POST", + headers: { + authorization: `Bearer ${session.sessionToken}`, + "content-type": "application/json", + accept: "application/json", + }, + body, + signal: AbortSignal.timeout(timeoutMs), + }); + if (!response.ok) { + return { sent: 0, error: `http_${response.status}` }; + } + return { sent: events.length }; + } + catch (error) { + return { sent: 0, error: error instanceof Error ? error.message.slice(0, 160) : "network_failed" }; + } +} +/** Fire-and-forget wrapper for sync call sites (never awaits into the caller's critical path). */ +export function scheduleAmsNotificationEvents(events, options = {}) { + void publishAmsNotificationEvents(events, options).catch(() => { + // publishAmsNotificationEvents is already fail-soft; this only guards a rejected promise from an inject. + }); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW1zLW5vdGlmaWNhdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhbXMtbm90aWZpY2F0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSw2R0FBNkc7QUFDN0csbUdBQW1HO0FBQ25HLDJHQUEyRztBQUMzRyw0RUFBNEU7QUFFNUUsT0FBTyxFQUFFLDZCQUE2QixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUE0QjdFLE1BQU0sQ0FBQyxNQUFNLG1DQUFtQyxHQUFHLE1BQU0sQ0FBQztBQUUxRCxTQUFTLGNBQWMsQ0FBQyxLQUFhO0lBQ25DLE9BQU8sS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3BDLENBQUM7QUFFRCxTQUFTLE1BQU07SUFDYixPQUFPLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDbEMsQ0FBQztBQUVELFNBQVMsbUJBQW1CLENBQUMsWUFBb0IsRUFBRSxXQUFtQjtJQUNwRSxPQUFPLHNCQUFzQixZQUFZLFdBQVcsV0FBVyxFQUFFLENBQUM7QUFDcEUsQ0FBQztBQUVELFNBQVMsa0JBQWtCLENBQUMsWUFBb0IsRUFBRSxVQUFrQjtJQUNsRSxPQUFPLHNCQUFzQixZQUFZLFNBQVMsVUFBVSxFQUFFLENBQUM7QUFDakUsQ0FBQztBQUVELE1BQU0sVUFBVSw2QkFBNkIsQ0FBQyxLQU03QztJQUNDLE1BQU0sY0FBYyxHQUFHLGNBQWMsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDNUQsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLFVBQVUsSUFBSSxNQUFNLEVBQUUsQ0FBQztJQUNoRCxPQUFPO1FBQ0wsU0FBUyxFQUFFLHFCQUFxQjtRQUNoQyxjQUFjO1FBQ2QsWUFBWSxFQUFFLEtBQUssQ0FBQyxZQUFZO1FBQ2hDLFVBQVUsRUFBRSxLQUFLLENBQUMsV0FBVztRQUM3QixRQUFRLEVBQUUsdUJBQXVCLEtBQUssQ0FBQyxZQUFZLElBQUksS0FBSyxD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 \ No newline at end of file diff --git a/packages/loopover-miner/lib/ams-notifications.ts b/packages/loopover-miner/lib/ams-notifications.ts new file mode 100644 index 0000000000..df1d0d3c0b --- /dev/null +++ b/packages/loopover-miner/lib/ams-notifications.ts @@ -0,0 +1,217 @@ +// AMS → hosted badge notifications (#7657). Builds DetectedNotificationEvent-shaped AMS kinds and POSTs them +// to the contributor ams-notifications ingest, which evaluates through evaluateNotificationEvent → +// notify-deliver (same handoff as src/queue/job-dispatch.ts). Fail-soft: a missing session or network blip +// never breaks the miner's real work. No parallel local notification store. +// +// Keep dedupKey / deeplink layout in lockstep with src/notifications/ams-events.ts (hosted twin — miner +// cannot import src/). + +import { resolveLoopoverBackendSession } from "./github-token-resolution.js"; + +export type AmsNotificationEventPayload = { + eventType: "ams_attempt_started" | "ams_attempt_failed" | "ams_governor_paused" | "ams_pr_outcome"; + recipientLogin: string; + repoFullName: string; + pullNumber: number; + dedupKey: string; + deeplink: string; + actorLogin: string; + detectedAt: string; +}; + +export type AmsNotificationPublishResult = { sent: number; error?: string }; + +export type AmsNotificationFetch = ( + url: string, + init?: { method?: string; headers?: Record; body?: string; signal?: AbortSignal }, +) => Promise; + +export type PublishAmsNotificationEventsOptions = { + env?: Record; + fetchFn?: AmsNotificationFetch; + timeoutMs?: number; + /** Test/self-host inject: mirrors job-dispatch evaluate → notify-deliver without HTTP. */ + dispatch?: (events: AmsNotificationEventPayload[]) => Promise; +}; + +export const DEFAULT_AMS_NOTIFICATION_TIMEOUT_MS = 10_000; + +function normalizeLogin(login: string): string { + return login.trim().toLowerCase(); +} + +function nowIso(): string { + return new Date().toISOString(); +} + +function githubIssueDeeplink(repoFullName: string, issueNumber: number): string { + return `https://github.com/${repoFullName}/issues/${issueNumber}`; +} + +function githubPullDeeplink(repoFullName: string, pullNumber: number): string { + return `https://github.com/${repoFullName}/pull/${pullNumber}`; +} + +export function buildAmsAttemptStartedPayload(input: { + recipientLogin: string; + repoFullName: string; + issueNumber: number; + attemptId: string; + detectedAt?: string; +}): AmsNotificationEventPayload { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + return { + eventType: "ams_attempt_started", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.issueNumber, + dedupKey: `ams_attempt_started:${input.repoFullName}#${input.issueNumber}:${input.attemptId}`, + deeplink: githubIssueDeeplink(input.repoFullName, input.issueNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} + +export function buildAmsAttemptFailedPayload(input: { + recipientLogin: string; + repoFullName: string; + issueNumber: number; + attemptId: string; + reason?: string | null; + detectedAt?: string; +}): AmsNotificationEventPayload { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const reasonKey = input.reason?.trim() ? `:${input.reason.trim().slice(0, 80)}` : ""; + return { + eventType: "ams_attempt_failed", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.issueNumber, + dedupKey: `ams_attempt_failed:${input.repoFullName}#${input.issueNumber}:${input.attemptId}${reasonKey}`, + deeplink: githubIssueDeeplink(input.repoFullName, input.issueNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} + +export function buildAmsGovernorPausedPayload(input: { + recipientLogin: string; + reason?: string | null; + pausedAt?: string; + detectedAt?: string; +}): AmsNotificationEventPayload { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const pausedAt = input.pausedAt ?? detectedAt; + const reasonKey = input.reason?.trim() ? `:${input.reason.trim().slice(0, 80)}` : ""; + return { + eventType: "ams_governor_paused", + recipientLogin, + repoFullName: "ams/governor", + pullNumber: 0, + dedupKey: `ams_governor_paused:${recipientLogin}:${pausedAt}${reasonKey}`, + deeplink: "https://github.com/JSONbored/loopover", + actorLogin: recipientLogin, + detectedAt, + }; +} + +export function buildAmsPrOutcomePayload(input: { + recipientLogin: string; + repoFullName: string; + pullNumber: number; + decision: "merged" | "closed"; + closedAt?: string | null; + detectedAt?: string; +}): AmsNotificationEventPayload { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const closedAt = input.closedAt?.trim() || detectedAt; + return { + eventType: "ams_pr_outcome", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.pullNumber, + // Same layout as src/notifications/ams-events.ts buildAmsPrOutcomeEvent. + dedupKey: `ams_pr_outcome:${input.decision}:${input.repoFullName}#${input.pullNumber}:${closedAt}`, + deeplink: githubPullDeeplink(input.repoFullName, input.pullNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} + +/** + * Publish AMS notification events through the hosted evaluate → notify-deliver path. Prefer an injected + * `dispatch` (tests / in-process self-host). Otherwise POST to `/v1/contributors/:login/ams-notifications` + * when a loopover-mcp session is on disk. Never throws. + */ +export async function publishAmsNotificationEvents( + events: AmsNotificationEventPayload[], + options: PublishAmsNotificationEventsOptions = {}, +): Promise { + if (!Array.isArray(events) || events.length === 0) return { sent: 0 }; + if (options.dispatch) { + try { + await options.dispatch(events); + return { sent: events.length }; + } catch (error) { + return { sent: 0, error: error instanceof Error ? error.message.slice(0, 160) : "dispatch_failed" }; + } + } + + const env = options.env ?? process.env; + const session = resolveLoopoverBackendSession(env as NodeJS.ProcessEnv); + if (!session) return { sent: 0, error: "no_session" }; + + const recipientLogin = normalizeLogin(events[0]!.recipientLogin); + if (!recipientLogin) return { sent: 0, error: "missing_recipient" }; + if (events.some((event) => normalizeLogin(event.recipientLogin) !== recipientLogin)) { + return { sent: 0, error: "mixed_recipients" }; + } + + const fetchFn = options.fetchFn ?? (fetch as AmsNotificationFetch); + const timeoutMs = options.timeoutMs ?? DEFAULT_AMS_NOTIFICATION_TIMEOUT_MS; + const url = `${session.apiUrl}/v1/contributors/${encodeURIComponent(recipientLogin)}/ams-notifications`; + const body = JSON.stringify({ + events: events.map(({ eventType, repoFullName, pullNumber, dedupKey, deeplink, actorLogin, detectedAt }) => ({ + eventType, + repoFullName, + pullNumber, + dedupKey, + deeplink, + actorLogin, + detectedAt, + })), + }); + + try { + const response = await fetchFn(url, { + method: "POST", + headers: { + authorization: `Bearer ${session.sessionToken}`, + "content-type": "application/json", + accept: "application/json", + }, + body, + signal: AbortSignal.timeout(timeoutMs), + }); + if (!response.ok) { + return { sent: 0, error: `http_${response.status}` }; + } + return { sent: events.length }; + } catch (error) { + return { sent: 0, error: error instanceof Error ? error.message.slice(0, 160) : "network_failed" }; + } +} + +/** Fire-and-forget wrapper for sync call sites (never awaits into the caller's critical path). */ +export function scheduleAmsNotificationEvents( + events: AmsNotificationEventPayload[], + options: PublishAmsNotificationEventsOptions = {}, +): void { + void publishAmsNotificationEvents(events, options).catch(() => { + // publishAmsNotificationEvents is already fail-soft; this only guards a rejected promise from an inject. + }); +} diff --git a/packages/loopover-miner/lib/attempt-cli.d.ts b/packages/loopover-miner/lib/attempt-cli.d.ts index da9eda8923..aaa447bd46 100644 --- a/packages/loopover-miner/lib/attempt-cli.d.ts +++ b/packages/loopover-miner/lib/attempt-cli.d.ts @@ -9,6 +9,7 @@ import type { resolveRejectionSignaled as ResolveRejectionSignaledFn } from "./r import type { cleanupAttemptWorktree as CleanupAttemptWorktreeFn, prepareAttemptWorktree as PrepareAttemptWorktreeFn } from "./attempt-worktree.js"; import type { SelfReviewContextFetch, fetchSelfReviewContext as FetchSelfReviewContextFn } from "./self-review-context.js"; import type { buildCodingTaskSpec as BuildCodingTaskSpecFn } from "./coding-task-spec.js"; +import { scheduleAmsNotificationEvents, type PublishAmsNotificationEventsOptions } from "./ams-notifications.js"; import type { resolveAmsPolicy as ResolveAmsPolicyFn } from "./ams-policy.js"; import type { checkMinerKillSwitch as CheckMinerKillSwitchFn } from "./governor-kill-switch.js"; import type { getAttemptHistory as GetAttemptHistoryFn } from "./portfolio-queue.js"; @@ -94,6 +95,8 @@ export type RunAttemptOptions = { /** Hosted soft-claim coordination at work-start/work-end, when the plane is enabled (#7168). Defaults to * discovery-index-client.js's own submitSoftClaim. */ submitSoftClaim?: typeof SubmitSoftClaimFn; + /** AMS badge notifications (#7657). Defaults to scheduleAmsNotificationEvents (session POST / inject). */ + scheduleAmsNotifications?: (events: Parameters[0], options?: PublishAmsNotificationEventsOptions) => void; /** Invoked with the real structured result at every return point, in addition to (never instead of) the * plain exit-code return -- the loop orchestrator's real hook into what actually happened. */ onResult?: (result: AttemptCliResult) => void; diff --git a/packages/loopover-miner/lib/attempt-cli.js b/packages/loopover-miner/lib/attempt-cli.js index 495a16a3d2..26675d63d8 100644 --- a/packages/loopover-miner/lib/attempt-cli.js +++ b/packages/loopover-miner/lib/attempt-cli.js @@ -32,6 +32,7 @@ import { cleanupAttemptWorktree, prepareAttemptWorktree } from "./attempt-worktr import { fetchSelfReviewContext } from "./self-review-context.js"; import { buildCodingTaskSpec } from "./coding-task-spec.js"; import { resolveAmsPolicy } from "./ams-policy.js"; +import { buildAmsAttemptFailedPayload, buildAmsAttemptStartedPayload, scheduleAmsNotificationEvents, } from "./ams-notifications.js"; import { checkMinerKillSwitch, recordMinerKillSwitchTransition } from "./governor-kill-switch.js"; import { captureMinerError } from "./sentry.js"; import { buildAttemptGovernorContext, buildAttemptLoopInput } from "./attempt-input-builder.js"; @@ -505,6 +506,16 @@ export async function runAttempt(args, options = {}) { await submitClaim(claimRecord, { env }); } const runAttemptPipeline = options.runMinerAttempt ?? runMinerAttempt; + const scheduleAmsNotifications = options.scheduleAmsNotifications ?? scheduleAmsNotificationEvents; + // AMS badge notify (#7657): attempt start — fire-and-forget through the hosted evaluate → deliver path. + scheduleAmsNotifications([ + buildAmsAttemptStartedPayload({ + recipientLogin: parsed.minerLogin, + repoFullName: parsed.repoFullName, + issueNumber: parsed.issueNumber, + attemptId, + }), + ], { env: env }); let result; try { result = await runAttemptPipeline({ @@ -528,9 +539,29 @@ export async function runAttempt(args, options = {}) { // `undefined` and the finally block's `?? true` default (meant for the earlier blocked paths that never // ran anything in the worktree) deleted it -- inverting shouldRetainWorktree's documented policy. worktreeResult.attemptOk = false; + scheduleAmsNotifications([ + buildAmsAttemptFailedPayload({ + recipientLogin: parsed.minerLogin, + repoFullName: parsed.repoFullName, + issueNumber: parsed.issueNumber, + attemptId, + reason: "attempt_crashed", + }), + ], { env: env }); throw error; } worktreeResult.attemptOk = result.outcome === "submitted"; + if (result.outcome !== "submitted") { + scheduleAmsNotifications([ + buildAmsAttemptFailedPayload({ + recipientLogin: parsed.minerLogin, + repoFullName: parsed.repoFullName, + issueNumber: parsed.issueNumber, + attemptId, + reason: result.outcome, + }), + ], { env: env }); + } // Real claim-conflict resolution (#4848): only meaningful once a real PR exists, so this only ever runs // on a real "submitted" outcome. checkSubmissionFreshness (inside runMinerAttempt) already caught the // common pre-submission case; this closes the narrower TOCTOU window where two miners raced past that @@ -695,4 +726,4 @@ export async function runAttempt(args, options = {}) { governorLedger?.close(); } } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXR0ZW1wdC1jbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhdHRlbXB0LWNsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvSEFBb0g7QUFDcEgscUdBQXFHO0FBQ3JHLDBHQUEwRztBQUMxRyw2R0FBNkc7QUFDN0csOEdBQThHO0FBQzlHLDJHQUEyRztBQUMzRyxxR0FBcUc7QUFDckcsMkZBQTJGO0FBQzNGLHFGQUFxRjtBQUNyRixFQUFFO0FBQ0YsMEdBQTBHO0FBQzFHLGtIQUFrSDtBQUNsSCxpSEFBaUg7QUFDakgsMkdBQTJHO0FBRTNHLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxnQ0FBZ0MsRUFBRSwyQ0FBMkMsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTlJLE9BQU8sRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNsRixPQUFPLEVBQUUsb0NBQW9DLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFcEQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFcEUsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXBELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVsRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXR0ZW1wdC1jbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhdHRlbXB0LWNsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvSEFBb0g7QUFDcEgscUdBQXFHO0FBQ3JHLDBHQUEwRztBQUMxRyw2R0FBNkc7QUFDN0csOEdBQThHO0FBQzlHLDJHQUEyRztBQUMzRyxxR0FBcUc7QUFDckcsMkZBQTJGO0FBQzNGLHFGQUFxRjtBQUNyRixFQUFFO0FBQ0YsMEdBQTBHO0FBQzFHLGtIQUFrSDtBQUNsSCxpSEFBaUg7QUFDakgsMkdBQTJHO0FBRTNHLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxnQ0FBZ0MsRUFBRSwyQ0FBMkMsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTlJLE9BQU8sRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNsRixPQUFPLEVBQUUsb0NBQW9DLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFcEQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFcEUsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXBELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVsRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFB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 \ No newline at end of file diff --git a/packages/loopover-miner/lib/attempt-cli.ts b/packages/loopover-miner/lib/attempt-cli.ts index 2367774f46..58751d181d 100644 --- a/packages/loopover-miner/lib/attempt-cli.ts +++ b/packages/loopover-miner/lib/attempt-cli.ts @@ -48,6 +48,12 @@ import type { SelfReviewContextFetch, fetchSelfReviewContext as FetchSelfReviewC import { buildCodingTaskSpec } from "./coding-task-spec.js"; import type { buildCodingTaskSpec as BuildCodingTaskSpecFn } from "./coding-task-spec.js"; import { resolveAmsPolicy } from "./ams-policy.js"; +import { + buildAmsAttemptFailedPayload, + buildAmsAttemptStartedPayload, + scheduleAmsNotificationEvents, + type PublishAmsNotificationEventsOptions, +} from "./ams-notifications.js"; import type { resolveAmsPolicy as ResolveAmsPolicyFn } from "./ams-policy.js"; import { checkMinerKillSwitch, recordMinerKillSwitchTransition } from "./governor-kill-switch.js"; import type { checkMinerKillSwitch as CheckMinerKillSwitchFn } from "./governor-kill-switch.js"; @@ -141,6 +147,11 @@ export type RunAttemptOptions = { /** Hosted soft-claim coordination at work-start/work-end, when the plane is enabled (#7168). Defaults to * discovery-index-client.js's own submitSoftClaim. */ submitSoftClaim?: typeof SubmitSoftClaimFn; + /** AMS badge notifications (#7657). Defaults to scheduleAmsNotificationEvents (session POST / inject). */ + scheduleAmsNotifications?: ( + events: Parameters[0], + options?: PublishAmsNotificationEventsOptions, + ) => void; /** Invoked with the real structured result at every return point, in addition to (never instead of) the * plain exit-code return -- the loop orchestrator's real hook into what actually happened. */ onResult?: (result: AttemptCliResult) => void; @@ -666,6 +677,19 @@ export async function runAttempt(args: string[], options: RunAttemptOptions = {} } const runAttemptPipeline = options.runMinerAttempt ?? runMinerAttempt; + const scheduleAmsNotifications = options.scheduleAmsNotifications ?? scheduleAmsNotificationEvents; + // AMS badge notify (#7657): attempt start — fire-and-forget through the hosted evaluate → deliver path. + scheduleAmsNotifications( + [ + buildAmsAttemptStartedPayload({ + recipientLogin: parsed.minerLogin, + repoFullName: parsed.repoFullName, + issueNumber: parsed.issueNumber, + attemptId, + }), + ], + { env: env as NodeJS.ProcessEnv }, + ); let result; try { result = await runAttemptPipeline( @@ -691,10 +715,36 @@ export async function runAttempt(args: string[], options: RunAttemptOptions = {} // `undefined` and the finally block's `?? true` default (meant for the earlier blocked paths that never // ran anything in the worktree) deleted it -- inverting shouldRetainWorktree's documented policy. worktreeResult.attemptOk = false; + scheduleAmsNotifications( + [ + buildAmsAttemptFailedPayload({ + recipientLogin: parsed.minerLogin, + repoFullName: parsed.repoFullName, + issueNumber: parsed.issueNumber, + attemptId, + reason: "attempt_crashed", + }), + ], + { env: env as NodeJS.ProcessEnv }, + ); throw error; } worktreeResult.attemptOk = result.outcome === "submitted"; + if (result.outcome !== "submitted") { + scheduleAmsNotifications( + [ + buildAmsAttemptFailedPayload({ + recipientLogin: parsed.minerLogin, + repoFullName: parsed.repoFullName, + issueNumber: parsed.issueNumber, + attemptId, + reason: result.outcome, + }), + ], + { env: env as NodeJS.ProcessEnv }, + ); + } // Real claim-conflict resolution (#4848): only meaningful once a real PR exists, so this only ever runs // on a real "submitted" outcome. checkSubmissionFreshness (inside runMinerAttempt) already caught the diff --git a/packages/loopover-miner/lib/governor-pause-cli.d.ts b/packages/loopover-miner/lib/governor-pause-cli.d.ts index 7677e385d4..a92a147248 100644 --- a/packages/loopover-miner/lib/governor-pause-cli.d.ts +++ b/packages/loopover-miner/lib/governor-pause-cli.d.ts @@ -1,4 +1,5 @@ import type { GovernorState } from "./governor-state.js"; +import { publishAmsNotificationEvents } from "./ams-notifications.js"; export type ParsedGovernorPauseArgs = { json: boolean; dryRun: boolean; @@ -19,6 +20,13 @@ export type ParsedGovernorNoArgsSubcommand = { }; export type GovernorPauseCliOptions = { openGovernorState?: () => GovernorState; + env?: Record; + /** Override AMS badge notify (#7657). Defaults to publishAmsNotificationEvents. */ + publishAmsNotifications?: typeof publishAmsNotificationEvents; + fetchSessionLogin?: (session: { + apiUrl: string; + sessionToken: string; + }) => Promise; }; export declare function parseGovernorPauseArgs(args: string[]): ParsedGovernorPauseArgs; export declare function parseGovernorResumeArgs(args: string[]): ParsedGovernorResumeArgs; diff --git a/packages/loopover-miner/lib/governor-pause-cli.js b/packages/loopover-miner/lib/governor-pause-cli.js index 698b749f48..389b018114 100644 --- a/packages/loopover-miner/lib/governor-pause-cli.js +++ b/packages/loopover-miner/lib/governor-pause-cli.js @@ -7,6 +7,8 @@ // same reasoning that table's other scalar fields (rate-limit buckets, cap usage) already rely on. import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js"; import { openGovernorState } from "./governor-state.js"; +import { buildAmsGovernorPausedPayload, publishAmsNotificationEvents, } from "./ams-notifications.js"; +import { resolveLoopoverBackendSession } from "./github-token-resolution.js"; const GOVERNOR_PAUSE_USAGE = "Usage: loopover-miner governor pause [--reason ] [--dry-run] [--json]"; const GOVERNOR_RESUME_USAGE = "Usage: loopover-miner governor resume [--dry-run] [--json]"; const GOVERNOR_STATUS_USAGE = "Usage: loopover-miner governor status [--json]"; @@ -79,6 +81,44 @@ function renderPauseState(pauseState) { const reason = pauseState.reason ? ` (${pauseState.reason})` : ""; return `governor is PAUSED since ${pauseState.pausedAt}${reason}`; } +async function resolveSessionLogin(env) { + const session = resolveLoopoverBackendSession(env); + if (!session) + return null; + try { + const response = await fetch(`${session.apiUrl}/v1/auth/session`, { + headers: { authorization: `Bearer ${session.sessionToken}`, accept: "application/json" }, + signal: AbortSignal.timeout(10_000), + }); + if (!response.ok) + return null; + const payload = (await response.json().catch(() => null)); + return typeof payload?.login === "string" && payload.login.trim() ? payload.login.trim() : null; + } + catch { + return null; + } +} +async function notifyGovernorPaused(pauseState, options) { + const env = options.env ?? process.env; + // Injected fetchSessionLogin (tests) may resolve a login without a disk session; only require a real + // session when falling back to GET /v1/auth/session. + const processEnv = env; + const login = options.fetchSessionLogin + ? await options.fetchSessionLogin(resolveLoopoverBackendSession(processEnv) ?? { apiUrl: "https://api.loopover.ai", sessionToken: "" }) + : await resolveSessionLogin(processEnv); + if (!login) + return; + const publish = options.publishAmsNotifications ?? publishAmsNotificationEvents; + const publishOptions = { env }; + await publish([ + buildAmsGovernorPausedPayload({ + recipientLogin: login, + reason: pauseState.reason, + ...(pauseState.pausedAt ? { pausedAt: pauseState.pausedAt } : {}), + }), + ], publishOptions); +} export async function runGovernorPause(args, options = {}) { const parsed = parseGovernorPauseArgs(args); if ("error" in parsed) { @@ -96,16 +136,16 @@ export async function runGovernorPause(args, options = {}) { return 0; } try { - return await withGovernorState(options, (governorState) => { - const pauseState = governorState.savePauseState({ paused: true, reason: parsed.reason }); - if (parsed.json) { - console.log(JSON.stringify(pauseState)); - } - else { - console.log(renderPauseState(pauseState)); - } - return 0; - }); + const pauseState = await withGovernorState(options, (governorState) => governorState.savePauseState({ paused: true, reason: parsed.reason })); + // AMS badge notify (#7657): best-effort; a notify miss must not fail the pause itself. + await notifyGovernorPaused(pauseState, options).catch(() => undefined); + if (parsed.json) { + console.log(JSON.stringify(pauseState)); + } + else { + console.log(renderPauseState(pauseState)); + } + return 0; } catch (error) { return reportCliFailure(parsed.json, describeCliError(error)); @@ -163,4 +203,4 @@ export async function runGovernorStatus(args, options = {}) { return reportCliFailure(parsed.json, describeCliError(error)); } } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ292ZXJub3ItcGF1c2UtY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZ292ZXJub3ItcGF1c2UtY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtHQUErRztBQUMvRywrR0FBK0c7QUFDL0csaUhBQWlIO0FBQ2pILCtHQUErRztBQUMvRyxvSEFBb0g7QUFDcEgsOEdBQThHO0FBQzlHLG1HQUFtRztBQUVuRyxPQUFPLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFHeEQsTUFBTSxvQkFBb0IsR0FBRyw2RUFBNkUsQ0FBQztBQUMzRyxNQUFNLHFCQUFxQixHQUFHLDREQUE0RCxDQUFDO0FBQzNGLE1BQU0scUJBQXFCLEdBQUcsZ0RBQWdELENBQUM7QUFjL0UsTUFBTSxVQUFVLHNCQUFzQixDQUFDLElBQWM7SUFDbkQsTUFBTSxPQUFPLEdBQThEO1FBQ3pFLElBQUksRUFBRSxLQUFLO1FBQ1gsTUFBTSxFQUFFLEtBQUs7UUFDYixNQUFNLEVBQUUsSUFBSTtLQUNiLENBQUM7SUFFRixLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxLQUFLLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDcEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzFCLElBQUksS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3ZCLE9BQU8sQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3BCLFNBQVM7UUFDWCxDQUFDO1FBQ0QscUZBQXFGO1FBQ3JGLElBQUksS0FBSyxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQzFCLE9BQU8sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1lBQ3RCLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFL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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ292ZXJub3ItcGF1c2UtY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZ292ZXJub3ItcGF1c2UtY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtHQUErRztBQUMvRywrR0FBK0c7QUFDL0csaUhBQWlIO0FBQ2pILCtHQUErRztBQUMvRyxvSEFBb0g7QUFDcEgsOEdBQThHO0FBQzlHLG1HQUFtRztBQUVuRyxPQUFPLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFeEQsT0FBTyxFQUNMLDZCQUE2QixFQUM3Qiw0QkFBNEIsR0FFN0IsTUFBTSx3QkFBd0IsQ0FBQztBQUNoQyxPQUFPLEVBQUUsNkJBQTZCLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUU3RSxNQUFNLG9CQUFvQixHQUFHLDZFQUE2RSxDQUFDO0FBQzNHLE1BQU0scUJBQXFCLEdBQUcsNERBQTRELENBQUM7QUFDM0YsTUFBTSxxQkFBcUIsR0FBRyxnREFBZ0QsQ0FBQztBQWtCL0UsTUFBTSxVQUFVLHNCQUFzQixDQUFDLElBQWM7SUFDbkQsTUFBTSxPQUFPLEdBQThEO1FBQ3pFLElBQUksRUFBRSxLQUFLO1FBQ1gsTUFBTSxFQUFFLEtBQUs7UUFDYixNQUFNLEVBQUUsSUFBSTtLQUNiLENBQUM7SUFFRixLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxLQUFLLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDcEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzFCLElBQUksS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3ZCLE9BQU8sQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3BCLFNBQVM7UUFDWCxDQUFDO1FBQ0QscUZBQXFGO1FB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 \ No newline at end of file diff --git a/packages/loopover-miner/lib/governor-pause-cli.ts b/packages/loopover-miner/lib/governor-pause-cli.ts index 2f28fc6a14..a4b8d11610 100644 --- a/packages/loopover-miner/lib/governor-pause-cli.ts +++ b/packages/loopover-miner/lib/governor-pause-cli.ts @@ -9,6 +9,12 @@ import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js"; import { openGovernorState } from "./governor-state.js"; import type { GovernorPauseState, GovernorState } from "./governor-state.js"; +import { + buildAmsGovernorPausedPayload, + publishAmsNotificationEvents, + type PublishAmsNotificationEventsOptions, +} from "./ams-notifications.js"; +import { resolveLoopoverBackendSession } from "./github-token-resolution.js"; const GOVERNOR_PAUSE_USAGE = "Usage: loopover-miner governor pause [--reason ] [--dry-run] [--json]"; const GOVERNOR_RESUME_USAGE = "Usage: loopover-miner governor resume [--dry-run] [--json]"; @@ -24,6 +30,10 @@ export type ParsedGovernorNoArgsSubcommand = { json: boolean } | { error: string export type GovernorPauseCliOptions = { openGovernorState?: () => GovernorState; + env?: Record; + /** Override AMS badge notify (#7657). Defaults to publishAmsNotificationEvents. */ + publishAmsNotifications?: typeof publishAmsNotificationEvents; + fetchSessionLogin?: (session: { apiUrl: string; sessionToken: string }) => Promise; }; export function parseGovernorPauseArgs(args: string[]): ParsedGovernorPauseArgs { @@ -101,6 +111,50 @@ function renderPauseState(pauseState: GovernorPauseState): string { return `governor is PAUSED since ${pauseState.pausedAt}${reason}`; } +async function resolveSessionLogin(env: NodeJS.ProcessEnv): Promise { + const session = resolveLoopoverBackendSession(env); + if (!session) return null; + try { + const response = await fetch(`${session.apiUrl}/v1/auth/session`, { + headers: { authorization: `Bearer ${session.sessionToken}`, accept: "application/json" }, + signal: AbortSignal.timeout(10_000), + }); + if (!response.ok) return null; + const payload = (await response.json().catch(() => null)) as { login?: unknown } | null; + return typeof payload?.login === "string" && payload.login.trim() ? payload.login.trim() : null; + } catch { + return null; + } +} + +async function notifyGovernorPaused( + pauseState: GovernorPauseState, + options: GovernorPauseCliOptions, +): Promise { + const env = options.env ?? process.env; + // Injected fetchSessionLogin (tests) may resolve a login without a disk session; only require a real + // session when falling back to GET /v1/auth/session. + const processEnv = env as NodeJS.ProcessEnv; + const login = options.fetchSessionLogin + ? await options.fetchSessionLogin( + resolveLoopoverBackendSession(processEnv) ?? { apiUrl: "https://api.loopover.ai", sessionToken: "" }, + ) + : await resolveSessionLogin(processEnv); + if (!login) return; + const publish = options.publishAmsNotifications ?? publishAmsNotificationEvents; + const publishOptions: PublishAmsNotificationEventsOptions = { env }; + await publish( + [ + buildAmsGovernorPausedPayload({ + recipientLogin: login, + reason: pauseState.reason, + ...(pauseState.pausedAt ? { pausedAt: pauseState.pausedAt } : {}), + }), + ], + publishOptions, + ); +} + export async function runGovernorPause(args: string[], options: GovernorPauseCliOptions = {}): Promise { const parsed = parseGovernorPauseArgs(args); if ("error" in parsed) { @@ -119,15 +173,17 @@ export async function runGovernorPause(args: string[], options: GovernorPauseCli } try { - return await withGovernorState(options, (governorState) => { - const pauseState = governorState.savePauseState({ paused: true, reason: parsed.reason }); - if (parsed.json) { - console.log(JSON.stringify(pauseState)); - } else { - console.log(renderPauseState(pauseState)); - } - return 0; - }); + const pauseState = await withGovernorState(options, (governorState) => + governorState.savePauseState({ paused: true, reason: parsed.reason }), + ); + // AMS badge notify (#7657): best-effort; a notify miss must not fail the pause itself. + await notifyGovernorPaused(pauseState, options).catch(() => undefined); + if (parsed.json) { + console.log(JSON.stringify(pauseState)); + } else { + console.log(renderPauseState(pauseState)); + } + return 0; } catch (error) { return reportCliFailure(parsed.json, describeCliError(error)); } diff --git a/packages/loopover-miner/lib/loop-cli.js b/packages/loopover-miner/lib/loop-cli.js index 2dc48b7156..0cafa5a34f 100644 --- a/packages/loopover-miner/lib/loop-cli.js +++ b/packages/loopover-miner/lib/loop-cli.js @@ -452,7 +452,7 @@ export async function runLoop(args, options = {}) { prNumber, decision: prDisposition.merged ? "merged" : "closed", closedAt: prDisposition.closedAt, - }, { eventLedger }); + }, { eventLedger, recipientLogin: parsed.minerLogin }); // Real per-repo reputation history (#5675): a resolved terminal outcome updates the decided/unfavorable // counts the Governor's self-reputation throttle reads on this repo's next attempt. `decided` always; // `unfavorable` only on a closed-without-merge (rejection-state-machine.js's isRejectedPr, matching @@ -529,4 +529,4 @@ export async function runLoop(args, options = {}) { runState.close(); } } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9vcC1jbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJsb29wLWNsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxzR0FBc0c7QUFDdEcsaUdBQWlHO0FBQ2pHLHlHQUF5RztBQUN6RyxtR0FBbUc7QUFDbkcsRUFBRTtBQUNGLHFHQUFxRztBQUNyRyx5R0FBeUc7QUFDekcscUZBQXFGO0FBQ3JGLDZHQUE2RztBQUM3RyxzREFBc0Q7QUFDdEQscUdBQXFHO0FBQ3JHLDBHQUEwRztBQUMxRyw4R0FBOEc7QUFDOUcsNkdBQTZHO0FBQzdHLDJEQUEyRDtBQUMzRCxFQUFFO0FBQ0YsdUdBQXVHO0FBQ3ZHLDBHQUEwRztBQUMxRyw4R0FBOEc7QUFDOUcsNEdBQTRHO0FBQzVHLCtHQUErRztBQUMvRyw2R0FBNkc7QUFDN0csK0VBQStFO0FBRS9FLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNsRixPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUV4RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUUxRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFcEQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFL0QsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFbkQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUU5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQkFBaUIsQ0FB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O1FBQ0QsSUFBSSxLQUFLLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDekIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDbEUsT0FBTyxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7WUFDdkIsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNYLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLEtBQUssZUFBZSxFQUFFLENBQUM7WUFDOUIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDbEUsT0FBTyxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUM7WUFDM0IsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNYLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDdkIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDbEUsT0FBTyxDQUFDLElBQUksR0FBRyxLQUFLLENBQUM7WUFDckIsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNYLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLEtBQUssY0FBYyxFQUFFLENBQUM7WUFDN0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9vcC1jbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJsb29wLWNsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxzR0FBc0c7QUFDdEcsaUdBQWlHO0FBQ2pHLHlHQUF5RztBQUN6RyxtR0FBbUc7QUFDbkcsRUFBRTtBQUNGLHFHQUFxRztBQUNyRyx5R0FBeUc7QUFDekcscUZBQXFGO0FBQ3JGLDZHQUE2RztBQUM3RyxzREFBc0Q7QUFDdEQscUdBQXFHO0FBQ3JHLDBHQUEwRztBQUMxRyw4R0FBOEc7QUFDOUcsNkdBQTZHO0FBQzdHLDJEQUEyRDtBQUMzRCxFQUFFO0FBQ0YsdUdBQXVHO0FBQ3ZHLDBHQUEwRztBQUMxRyw4R0FBOEc7QUFDOUcsNEdBQTRHO0FBQzVHLCtHQUErRztBQUMvRyw2R0FBNkc7QUFDN0csK0VBQStFO0FBRS9FLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNsRixPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUV4RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUUxRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFcEQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFL0QsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFbkQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUU5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQkFBaUIsQ0FB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O1FBQ0QsSUFBSSxLQUFLLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDekIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDbEUsT0FBTyxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7WUFDdkIsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNYLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLEtBQUssZUFBZSxFQUFFLENBQUM7WUFDOUIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDbEUsT0FBTyxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUM7WUFDM0IsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNYLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDdkIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDbEUsT0FBTyxDQUFDLElBQUksR0FBRyxLQUFLLENBQUM7WUFDckIsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNYLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLEtBQUssY0FBYyxFQUFFLENBQUM7WUFDN0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FB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 \ No newline at end of file diff --git a/packages/loopover-miner/lib/loop-cli.ts b/packages/loopover-miner/lib/loop-cli.ts index 676362879a..7b8037047e 100644 --- a/packages/loopover-miner/lib/loop-cli.ts +++ b/packages/loopover-miner/lib/loop-cli.ts @@ -567,7 +567,7 @@ export async function runLoop(args: string[], options: RunLoopOptions = {}): Pro decision: prDisposition.merged ? "merged" : "closed", closedAt: prDisposition.closedAt, }, - { eventLedger }, + { eventLedger, recipientLogin: parsed.minerLogin }, ); // Real per-repo reputation history (#5675): a resolved terminal outcome updates the decided/unfavorable // counts the Governor's self-reputation throttle reads on this repo's next attempt. `decided` always; diff --git a/packages/loopover-miner/lib/pr-outcome.d.ts b/packages/loopover-miner/lib/pr-outcome.d.ts index 81e7d723bd..562d10dc14 100644 --- a/packages/loopover-miner/lib/pr-outcome.d.ts +++ b/packages/loopover-miner/lib/pr-outcome.d.ts @@ -1,4 +1,5 @@ import type { AppendEventInput, LedgerEntry } from "./event-ledger.js"; +import { scheduleAmsNotificationEvents, type PublishAmsNotificationEventsOptions } from "./ams-notifications.js"; /** Event-ledger vocabulary for a miner-local PR outcome. */ export declare const MINER_PR_OUTCOME_EVENT: "pr_outcome"; /** The terminal decisions a miner records for one of its own PRs. */ @@ -24,6 +25,10 @@ export type RecordPrOutcomeOptions = { eventLedger?: { appendEvent(event: AppendEventInput): LedgerEntry; }; + /** Recipient for AMS badge notify (#7657). When absent, notification is skipped (ledger write still happens). */ + recipientLogin?: string; + env?: Record; + scheduleAmsNotifications?: (events: Parameters[0], options?: PublishAmsNotificationEventsOptions) => void; }; export type PrOutcomeLedgerReader = { readEvents(filter?: { diff --git a/packages/loopover-miner/lib/pr-outcome.js b/packages/loopover-miner/lib/pr-outcome.js index a4ec93d12f..afa37df93e 100644 --- a/packages/loopover-miner/lib/pr-outcome.js +++ b/packages/loopover-miner/lib/pr-outcome.js @@ -9,6 +9,7 @@ // its own PRs (it may have no webhook relay at all): same concept name, different codebase layer, no shared code. // The distinct `MINER_PR_OUTCOME_EVENT` local constant keeps the two from being conflated. import { REJECTION_REASONS } from "./rejection-templates.js"; +import { buildAmsPrOutcomePayload, scheduleAmsNotificationEvents, } from "./ams-notifications.js"; /** Event-ledger vocabulary for a miner-local PR outcome. */ export const MINER_PR_OUTCOME_EVENT = "pr_outcome"; /** The terminal decisions a miner records for one of its own PRs. */ @@ -69,7 +70,22 @@ export function recordPrOutcomeSnapshot(input, options = {}) { }); if (!payload) return null; - return eventLedger.appendEvent({ type: MINER_PR_OUTCOME_EVENT, repoFullName, payload }); + const entry = eventLedger.appendEvent({ type: MINER_PR_OUTCOME_EVENT, repoFullName, payload }); + // AMS badge notify (#7657): fire-and-forget when a recipient login is known (loop-cli passes minerLogin). + const recipientLogin = typeof options.recipientLogin === "string" ? options.recipientLogin.trim() : ""; + if (recipientLogin) { + const schedule = options.scheduleAmsNotifications ?? scheduleAmsNotificationEvents; + schedule([ + buildAmsPrOutcomePayload({ + recipientLogin, + repoFullName, + pullNumber: payload.prNumber, + decision: payload.decision, + closedAt: payload.closedAt, + }), + ], { env: options.env ?? process.env }); + } + return entry; } /** * Reconstruct the latest outcome per repo/PR from the ledger's ascending append-only event stream (mirrors @@ -102,4 +118,4 @@ export function readPrOutcomes(eventLedger, filter = {}) { } return latest; } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHItb3V0Y29tZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInByLW91dGNvbWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsaUhBQWlIO0FBQ2pILDBHQUEwRztBQUMxRyxpSEFBaUg7QUFDakgsOEJBQThCO0FBQzlCLEVBQUU7QUFDRiw2R0FBNkc7QUFDN0csK0dBQStHO0FBQy9HLDhHQUE4RztBQUM5RyxrSEFBa0g7QUFDbEgsMkZBQTJGO0FBRTNGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRzdELDREQUE0RDtBQUM1RCxNQUFNLENBQUMsTUFBTSxzQkFBc0IsR0FBRyxZQUFxQixDQUFDO0FBRTVELHFFQUFxRTtBQUNyRSxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBVSxDQUFDLENBQUM7QUE4QnZGLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxDQUFTLDBCQUEwQixDQUFDLENBQUM7QUFDaEUsTUFBTSxTQUFTLEdBQUcsSUFBSSxHQUFHLENBQVMsaUJBQWlCLENBQUMsQ0FBQztBQUVyRCxTQUFTLGNBQWMsQ0FBQyxLQUFjO0lBQ3BDLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxLQUFLLEtBQUssSUFBSTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3ZELElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzNDLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUM3QixPQUFPLE9BQU8sSUFBSSxJQUFJLENBQUM7QUFDekIsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLHlCQUF5QixDQUFDLE9BQWdCO0lBQ3hELElBQUksQ0FBQyxPQUFPLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDO1FB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHItb3V0Y29tZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInByLW91dGNvbWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsaUhBQWlIO0FBQ2pILDBHQUEwRztBQUMxRyxpSEFBaUg7QUFDakgsOEJBQThCO0FBQzlCLEVBQUU7QUFDRiw2R0FBNkc7QUFDN0csK0dBQStHO0FBQy9HLDhHQUE4RztBQUM5RyxrSEFBa0g7QUFDbEgsMkZBQTJGO0FBRTNGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTdELE9BQU8sRUFDTCx3QkFBd0IsRUFDeEIsNkJBQTZCLEdBRTlCLE1BQU0sd0JBQXdCLENBQUM7QUFFaEMsNERBQTREO0FBQzVELE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLFlBQXFCLENBQUM7QUFFNUQscUVBQXFFO0FBQ3JFLE1BQU0sQ0FBQyxNQUFNLDBCQUEwQixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFVLENBQUMsQ0FBQztBQXFDdkYsTUFBTSxXQUFXLEdBQUcsSUFBSSxHQUFHLENBQVMsMEJBQTBCLENBQUMsQ0FBQztBQUNoRSxNQUFNLFNBQVMsR0FBRyxJQUFJLEdBQUcsQ0FBUyxpQkFBaUIsQ0FBQyxDQUFDO0FBRXJELFNBQVMsY0FBYyxDQUFDLEtBQWM7SUFDcEMsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDdkQsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDM0MsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzdCLE9BQU8sT0FBTyxJQUFJLElBQUksQ0FBQztBQUN6QixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUseUJBQXlCLENBQUMsT0FBZ0I7SUFDeEQsSUFBSSxDQUFDLE9BQU8sSUFBSSxPQUFPLE9BQU8sS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNuRixNQUFNLE1BQU0sR0FBRyxPQUFrQyxDQUFDO0lBQ2xELElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSyxNQUFNLENBQUMsUUFBbUIsSUFBSSxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDeEYsTUFBTSxRQUFRLEdBQUcsY0FBYyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNqRCxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUN6RCxNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2hELE1BQU0sTUFBTSxHQUFHLFFBQVEsS0FBSyxRQUFRLElBQUksU0FBUyxLQUFLLElBQUksSUFBSSxTQUFTLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUMxRyxPQUFPO1FBQ0wsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFrQjtRQUNuQyxRQUFRLEVBQUUsUUFBa0M7UUFDNUMsUUFBUSxFQUFFLGNBQWMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDO1FBQ3pDLE1BQU07S0FDUCxDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSx1QkFBdUIsQ0FBQyxLQUFxQixFQUFFLFVBQWtDLEVBQUU7SUFDakcsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQztJQUN4QyxJQUFJLENBQUMsV0FBVyxJQUFJLE9BQU8sV0FBVyxDQUFDLFdBQVcsS0FBSyxVQUFVO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQzNHLE1BQU0sWUFBWSxHQUFHLE9BQU8sS0FBSyxDQUFDLFlBQVksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUM3RixJQUFJLENBQUMsWUFBWTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQy9CLE1BQU0sT0FBTyxHQUFHLHlCQUF5QixDQUFDO1FBQ3hDLFFBQVEsRUFBRSxLQUFLLENBQUMsUUFBUTtRQUN4QixRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVE7UUFDeEIsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRO1FBQ3hCLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTTtLQUNyQixDQUFDLENBQUM7SUFDSCxJQUFJLENBQUMsT0FBTztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzFCLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxXQUFXLENBQUMsRUFBRSxJQUFJLEVBQUUsc0JBQXNCLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDL0YsMEdBQTBHO0lBQzFHLE1BQU0sY0FBYyxHQUFHLE9BQU8sT0FBTyxDQUFDLGNBQWMsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUN2RyxJQUFJLGNBQWMsRUFBRSxDQUFDO1FBQ25CLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsSUFBSSw2QkFBNkIsQ0FBQztRQUNuRixRQUFRLENBQ047WUFDRSx3QkFBd0IsQ0FBQztnQkFDdkIsY0FBYztnQkFDZCxZQUFZO2dCQUNaLFVBQVUsRUFBRSxPQUFPLENBQUMsUUFBUTtnQkFDNUIsUUFBUSxFQUFFLE9BQU8sQ0FBQyxRQUFRO2dCQUMxQixRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7YUFDM0IsQ0FBQztTQUNILEVBQ0QsRUFBRSxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUcsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQ3BDLENBQUM7SUFDSixDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUM1QixXQUFxRCxFQUNyRCxTQUFvRCxFQUFFO0lBRXRELE1BQU0sTUFBTSxHQUFHLFdBQVcsSUFBSSxPQUFPLFdBQVcsQ0FBQyxVQUFVLEtBQUssVUFBVSxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDakgsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQWlFLENBQUM7SUFDeEYsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3hELElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtZQUFFLFNBQVM7UUFDbEQsTUFBTSxHQUFHLEdBQUcsS0FBc0UsQ0FBQztRQUNuRixJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssc0JBQXNCO1lBQUUsU0FBUztRQUNsRCxJQUFJLE9BQU8sR0FBRyxDQUFDLFlBQVksS0FBSyxRQUFRLElBQUksQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRTtZQUFFLFNBQVM7UUFDL0UsTUFBTSxVQUFVLEdBQUcseUJBQXlCLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELElBQUksQ0FBQyxVQUFVO1lBQUUsU0FBUztRQUMxQiw0R0FBNEc7UUFDNUcsK0dBQStHO1FBQy9HLDhHQUE4RztRQUM5RywyR0FBMkc7UUFDM0csK0dBQStHO1FBQy9HLE1BQU0sR0FBRyxHQUFHLEdBQUcsR0FBRyxDQUFDLFlBQVksSUFBSSxVQUFVLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDekQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixNQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsVUFBVSxFQUFFLFlBQVksRUFBRSxHQUFHLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyJ9 \ No newline at end of file diff --git a/packages/loopover-miner/lib/pr-outcome.ts b/packages/loopover-miner/lib/pr-outcome.ts index 274df98712..ad78ddaf32 100644 --- a/packages/loopover-miner/lib/pr-outcome.ts +++ b/packages/loopover-miner/lib/pr-outcome.ts @@ -11,6 +11,11 @@ import { REJECTION_REASONS } from "./rejection-templates.js"; import type { AppendEventInput, LedgerEntry } from "./event-ledger.js"; +import { + buildAmsPrOutcomePayload, + scheduleAmsNotificationEvents, + type PublishAmsNotificationEventsOptions, +} from "./ams-notifications.js"; /** Event-ledger vocabulary for a miner-local PR outcome. */ export const MINER_PR_OUTCOME_EVENT = "pr_outcome" as const; @@ -40,6 +45,13 @@ export type RecordPrOutcomeOptions = { * writer throws `invalid_event_ledger` at runtime when this is absent or lacks `appendEvent`. Reuses the * real EventLedger#appendEvent signature so a genuine EventLedger (not just a same-shaped stub) type-checks. */ eventLedger?: { appendEvent(event: AppendEventInput): LedgerEntry }; + /** Recipient for AMS badge notify (#7657). When absent, notification is skipped (ledger write still happens). */ + recipientLogin?: string; + env?: Record; + scheduleAmsNotifications?: ( + events: Parameters[0], + options?: PublishAmsNotificationEventsOptions, + ) => void; }; export type PrOutcomeLedgerReader = { @@ -97,7 +109,25 @@ export function recordPrOutcomeSnapshot(input: PrOutcomeInput, options: RecordPr reason: input.reason, }); if (!payload) return null; - return eventLedger.appendEvent({ type: MINER_PR_OUTCOME_EVENT, repoFullName, payload }); + const entry = eventLedger.appendEvent({ type: MINER_PR_OUTCOME_EVENT, repoFullName, payload }); + // AMS badge notify (#7657): fire-and-forget when a recipient login is known (loop-cli passes minerLogin). + const recipientLogin = typeof options.recipientLogin === "string" ? options.recipientLogin.trim() : ""; + if (recipientLogin) { + const schedule = options.scheduleAmsNotifications ?? scheduleAmsNotificationEvents; + schedule( + [ + buildAmsPrOutcomePayload({ + recipientLogin, + repoFullName, + pullNumber: payload.prNumber, + decision: payload.decision, + closedAt: payload.closedAt, + }), + ], + { env: options.env ?? process.env }, + ); + } + return entry; } /** diff --git a/src/api/routes.ts b/src/api/routes.ts index 6df0b5be06..8f6e788bf3 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -283,7 +283,8 @@ import { attachDataQuality, buildCoreSignalFidelity, buildFreshnessSloReport, bu import { buildContributorOpenPrMonitor } from "../signals/contributor-open-pr-monitor"; import { buildContributorPrOutcomes } from "../signals/contributor-pr-outcomes"; import { buildReviewRiskExplanation } from "../signals/review-risk"; -import { buildNotificationFeed } from "../notifications/service"; +import { buildNotificationFeed, evaluateAndEnqueueNotificationDeliveries } from "../notifications/service"; +import { normalizeAmsNotificationEventInput } from "../notifications/ams-events"; import { buildPullRequestReviewability, type PullRequestReviewability } from "../signals/reward-risk"; import { buildLocalBranchAnalysis, findCurrentBranchPullRequest } from "../signals/local-branch"; import { buildIssueSlopAssessment } from "../signals/issue-slop"; @@ -467,6 +468,24 @@ const markNotificationsReadBodySchema = z.object({ ids: z.array(z.string().min(1).max(MAX_NOTIFICATION_DELIVERY_ID_LENGTH)).max(MAX_NOTIFICATION_MARK_READ_IDS).optional(), }); +// #7657: AMS miner posts DetectedNotificationEvent-shaped AMS kinds; recipient is forced to the path login. +const amsNotificationsBodySchema = z.object({ + events: z + .array( + z.object({ + eventType: z.enum(["ams_attempt_started", "ams_attempt_failed", "ams_governor_paused", "ams_pr_outcome"]), + repoFullName: z.string().min(1).max(200), + pullNumber: z.number().int().min(0), + dedupKey: z.string().min(1).max(500), + deeplink: z.string().min(1).max(2000), + actorLogin: z.string().min(1).max(100), + detectedAt: z.string().min(1).max(64), + }), + ) + .min(1) + .max(20), +}); + // #6746: body of POST/DELETE /v1/contributors/:login/watches. Mirrors watchIssuesShape (src/mcp/server.ts) minus // `login` (path param) and `action` (the HTTP verb). `labels` is POST-only (a DELETE ignores it). const watchSubscriptionBodySchema = z.object({ @@ -3517,6 +3536,27 @@ export function createApp() { return c.json({ login: login.toLowerCase(), marked }); }); + // #7657: AMS miner (or any self-scoped session) posts AMS-relevant notification events. Events are forced onto + // the path login and evaluated through evaluateAndEnqueueNotificationDeliveries — the same + // evaluateNotificationEvent → notify-deliver handoff job-dispatch.ts uses for webhook kinds. + app.post("/v1/contributors/:login/ams-notifications", async (c) => { + const login = c.req.param("login"); + const unauthorized = await requireContributorAccess(c, login); + if (unauthorized) return unauthorized; + const parsed = amsNotificationsBodySchema.safeParse(await c.req.json().catch(() => null)); + if (!parsed.success) return c.json({ error: "invalid_ams_notifications", issues: parsed.error.issues }, 400); + const events = parsed.data.events + .map((raw) => normalizeAmsNotificationEventInput(raw, login)) + .filter((event): event is NonNullable => event !== null); + if (events.length === 0) return c.json({ error: "invalid_ams_notifications", detail: "no_valid_events" }, 400); + const deliveries = await evaluateAndEnqueueNotificationDeliveries(c.env, events); + return c.json({ + login: login.toLowerCase(), + accepted: events.length, + enqueued: deliveries.length, + }); + }); + // #6746: REST mirror of the `loopover_watch_issues` MCP tool (LoopoverMcp.watchIssues) — manage a contributor's // own issue-watch subscriptions. The MCP tool's `action` enum splits across the HTTP verbs: GET=list, POST=watch, // DELETE=unwatch. Every verb is self-scoped via requireContributorAccess (a session may only touch its own diff --git a/src/db/schema.ts b/src/db/schema.ts index 56b16d01ea..d9ac9176dd 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1126,6 +1126,9 @@ export const notificationSubscriptions = sqliteTable( }), ); +// event_type is free-text: webhook kinds (pull_request_changes_requested / pull_request_merged / +// issue_watch_match) plus AMS kinds (#7657: ams_attempt_started / ams_attempt_failed / +// ams_governor_paused / ams_pr_outcome). Subscriptions stay channel-scoped; kind filtering is at evaluate time. export const notificationDeliveries = sqliteTable( "notification_deliveries", { diff --git a/src/notifications/ams-events.ts b/src/notifications/ams-events.ts new file mode 100644 index 0000000000..01680940ea --- /dev/null +++ b/src/notifications/ams-events.ts @@ -0,0 +1,164 @@ +// AMS → badge notification bridge (#7657). Pure builders for DetectedNotificationEvent rows that the miner +// (and the session-authenticated ingest route) feed into evaluateNotificationEvent → notify-deliver — the same +// path job-dispatch.ts uses for webhook-detected kinds. No parallel delivery store. +// +// Payload/dedupKey shape is mirrored in packages/loopover-miner/lib/ams-notifications.ts (miner cannot import +// src/); keep the two builders in lockstep when changing eventType strings or dedupKey layout. + +import type { DetectedNotificationEvent, NotificationEventType } from "../types"; +import { nowIso } from "../utils/json"; + +export const AMS_NOTIFICATION_EVENT_TYPES = [ + "ams_attempt_started", + "ams_attempt_failed", + "ams_governor_paused", + "ams_pr_outcome", +] as const satisfies readonly NotificationEventType[]; + +export type AmsNotificationEventType = (typeof AMS_NOTIFICATION_EVENT_TYPES)[number]; + +const AMS_EVENT_TYPE_SET = new Set(AMS_NOTIFICATION_EVENT_TYPES); + +export function isAmsNotificationEventType(value: unknown): value is AmsNotificationEventType { + return typeof value === "string" && AMS_EVENT_TYPE_SET.has(value); +} + +function normalizeLogin(login: string): string { + return login.trim().toLowerCase(); +} + +function githubIssueDeeplink(repoFullName: string, issueNumber: number): string { + return `https://github.com/${repoFullName}/issues/${issueNumber}`; +} + +function githubPullDeeplink(repoFullName: string, pullNumber: number): string { + return `https://github.com/${repoFullName}/pull/${pullNumber}`; +} + +/** Attempt start — `pullNumber` carries the ISSUE number (same overload issue_watch_match uses). */ +export function buildAmsAttemptStartedEvent(input: { + recipientLogin: string; + repoFullName: string; + issueNumber: number; + attemptId: string; + detectedAt?: string; +}): DetectedNotificationEvent { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + return { + eventType: "ams_attempt_started", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.issueNumber, + dedupKey: `ams_attempt_started:${input.repoFullName}#${input.issueNumber}:${input.attemptId}`, + deeplink: githubIssueDeeplink(input.repoFullName, input.issueNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} + +/** Attempt fail — same issue-number overload as start. */ +export function buildAmsAttemptFailedEvent(input: { + recipientLogin: string; + repoFullName: string; + issueNumber: number; + attemptId: string; + reason?: string | null; + detectedAt?: string; +}): DetectedNotificationEvent { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const reasonKey = input.reason?.trim() ? `:${input.reason.trim().slice(0, 80)}` : ""; + return { + eventType: "ams_attempt_failed", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.issueNumber, + dedupKey: `ams_attempt_failed:${input.repoFullName}#${input.issueNumber}:${input.attemptId}${reasonKey}`, + deeplink: githubIssueDeeplink(input.repoFullName, input.issueNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} + +/** + * Governor pause — not PR-scoped. `repoFullName` is a stable synthetic scope (`ams/governor`); `pullNumber` is 0. + */ +export function buildAmsGovernorPausedEvent(input: { + recipientLogin: string; + reason?: string | null; + pausedAt?: string; + detectedAt?: string; +}): DetectedNotificationEvent { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const pausedAt = input.pausedAt ?? detectedAt; + const reasonKey = input.reason?.trim() ? `:${input.reason.trim().slice(0, 80)}` : ""; + return { + eventType: "ams_governor_paused", + recipientLogin, + repoFullName: "ams/governor", + pullNumber: 0, + dedupKey: `ams_governor_paused:${recipientLogin}:${pausedAt}${reasonKey}`, + deeplink: "https://github.com/JSONbored/loopover", + actorLogin: recipientLogin, + detectedAt, + }; +} + +/** Miner-local PR outcome change (merged or closed). */ +export function buildAmsPrOutcomeEvent(input: { + recipientLogin: string; + repoFullName: string; + pullNumber: number; + decision: "merged" | "closed"; + closedAt?: string | null; + detectedAt?: string; +}): DetectedNotificationEvent { + const recipientLogin = normalizeLogin(input.recipientLogin); + const detectedAt = input.detectedAt ?? nowIso(); + const closedAt = input.closedAt?.trim() || detectedAt; + return { + eventType: "ams_pr_outcome", + recipientLogin, + repoFullName: input.repoFullName, + pullNumber: input.pullNumber, + // decision sits immediately after the eventType so buildAmsPrOutcomeNotification can read it without + // scanning an opaque middle segment (ams_pr_outcome:{merged|closed}:{repo}#{n}:{closedAt}). + dedupKey: `ams_pr_outcome:${input.decision}:${input.repoFullName}#${input.pullNumber}:${closedAt}`, + deeplink: githubPullDeeplink(input.repoFullName, input.pullNumber), + actorLogin: recipientLogin, + detectedAt, + }; +} + +/** + * Validate a miner-posted AMS event payload and stamp the authenticated recipient. Rejects non-AMS kinds so + * this ingest cannot forge webhook notification types. + */ +export function normalizeAmsNotificationEventInput( + raw: unknown, + recipientLogin: string, +): DetectedNotificationEvent | null { + if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null; + const record = raw as Record; + if (!isAmsNotificationEventType(record.eventType)) return null; + if (typeof record.repoFullName !== "string" || !record.repoFullName.trim()) return null; + if (typeof record.dedupKey !== "string" || !record.dedupKey.trim()) return null; + if (typeof record.deeplink !== "string" || !record.deeplink.trim()) return null; + if (typeof record.actorLogin !== "string" || !record.actorLogin.trim()) return null; + if (typeof record.detectedAt !== "string" || !record.detectedAt.trim()) return null; + if (!Number.isInteger(record.pullNumber) || (record.pullNumber as number) < 0) return null; + const login = normalizeLogin(recipientLogin); + return { + eventType: record.eventType, + recipientLogin: login, + repoFullName: record.repoFullName.trim(), + pullNumber: record.pullNumber as number, + dedupKey: record.dedupKey.trim(), + deeplink: record.deeplink.trim(), + // Force actor to the authenticated recipient — never trust a client-supplied actorLogin. + actorLogin: login, + detectedAt: record.detectedAt.trim(), + }; +} diff --git a/src/notifications/service.ts b/src/notifications/service.ts index 826e2de800..6f3f34a2b9 100644 --- a/src/notifications/service.ts +++ b/src/notifications/service.ts @@ -10,7 +10,13 @@ import { } from "../db/repositories"; import { isGrabbableHighMultiplierIssue } from "../signals/engine"; import { canLoginAccessRepo } from "../services/control-panel-roles"; -import type { DetectedNotificationEvent, IssueRecord, NotificationChannel, NotificationDeliveryRecord, NotificationSubscriptionRecord } from "../types"; +import type { + DetectedNotificationEvent, + IssueRecord, + NotificationChannel, + NotificationDeliveryRecord, + NotificationSubscriptionRecord, +} from "../types"; import { nowIso } from "../utils/json"; // Per-recipient, per-channel safety cap. The killer event (changes_requested) delivers immediately, but a @@ -54,6 +60,52 @@ export function buildIssueWatchNotification(event: DetectedNotificationEvent): { }; } +// AMS (#7657): attempt lifecycle — `pullNumber` carries the ISSUE number. Public-safe; no reward/trust figures. +export function buildAmsAttemptStartedNotification(event: DetectedNotificationEvent): { title: string; body: string } { + const ref = `${event.repoFullName}#${event.pullNumber}`; + return { + title: sanitizePublicComment(`Attempt started on ${ref}`), + body: sanitizePublicComment(`Your AMS miner started an attempt on ${ref}. Watch the attempt log for progress and the next decision.`), + }; +} + +export function buildAmsAttemptFailedNotification(event: DetectedNotificationEvent): { title: string; body: string } { + const ref = `${event.repoFullName}#${event.pullNumber}`; + return { + title: sanitizePublicComment(`Attempt failed on ${ref}`), + body: sanitizePublicComment(`Your AMS miner attempt on ${ref} did not complete successfully. Check the attempt log, then reclaim or pick the next high-fit issue.`), + }; +} + +export function buildAmsGovernorPausedNotification(_event: DetectedNotificationEvent): { title: string; body: string } { + return { + title: sanitizePublicComment("AMS governor paused"), + body: sanitizePublicComment( + "Your AMS governor is paused. New attempt cycles will wait until you resume with `loopover-miner governor resume`.", + ), + }; +} + +export function buildAmsPrOutcomeNotification(event: DetectedNotificationEvent): { title: string; body: string } { + const ref = `${event.repoFullName}#${event.pullNumber}`; + // dedupKey layout from buildAmsPrOutcomeEvent: ams_pr_outcome:{merged|closed}:{repo}#{n}:{closedAt} + const merged = event.dedupKey.startsWith("ams_pr_outcome:merged:"); + if (merged) { + return { + title: sanitizePublicComment(`AMS recorded merge: ${ref}`), + body: sanitizePublicComment( + `Your AMS miner recorded that ${ref} merged. Merged work strengthens your standing on ${event.repoFullName} — check your decision pack for the next high-fit issue.`, + ), + }; + } + return { + title: sanitizePublicComment(`AMS recorded close: ${ref}`), + body: sanitizePublicComment( + `Your AMS miner recorded that ${ref} closed without merge. Review the feedback, then pick the next high-fit issue on ${event.repoFullName}.`, + ), + }; +} + // Maps a detected event to its public-safe notification content. export function buildNotificationContent(event: DetectedNotificationEvent): { title: string; body: string } { switch (event.eventType) { @@ -61,11 +113,44 @@ export function buildNotificationContent(event: DetectedNotificationEvent): { ti return buildMergedOutcomeNotification(event); case "issue_watch_match": return buildIssueWatchNotification(event); - default: + case "ams_attempt_started": + return buildAmsAttemptStartedNotification(event); + case "ams_attempt_failed": + return buildAmsAttemptFailedNotification(event); + case "ams_governor_paused": + return buildAmsGovernorPausedNotification(event); + case "ams_pr_outcome": + return buildAmsPrOutcomeNotification(event); + case "pull_request_changes_requested": return buildChangesRequestedNotification(event); } } +/** + * Mirrors `job-dispatch.ts`'s notify-evaluate → notify-deliver handoff: evaluate each event, then enqueue one + * `notify-deliver` job per freshly-created pending delivery. Used by the AMS ingest route (#7657) and kept + * identical in shape to the queue processor so AMS kinds never take a parallel path. + */ +export async function evaluateAndEnqueueNotificationDeliveries( + env: Env, + events: DetectedNotificationEvent[], +): Promise { + const pending: NotificationDeliveryRecord[] = []; + for (const event of events) { + pending.push(...(await evaluateNotificationEvent(env, event))); + } + await Promise.all( + pending.map((delivery) => + env.JOBS.send({ + type: "notify-deliver", + requestedBy: "notify-evaluate", + deliveryId: delivery.id, + }), + ), + ); + return pending; +} + /** * #699 path B: when a webhook opens a NEW grabbable, high-multiplier issue, fan out one notification event * per watching miner (matching their optional label filter), skipping the issue's own author. DB-backed diff --git a/src/openapi/schemas.ts b/src/openapi/schemas.ts index 3796b8ff3c..3d90f80cb7 100644 --- a/src/openapi/schemas.ts +++ b/src/openapi/schemas.ts @@ -519,6 +519,14 @@ export const NotificationsMarkedSchema = z }) .openapi("NotificationsMarked"); +export const AmsNotificationsAcceptedSchema = z + .object({ + login: z.string(), + accepted: z.number(), + enqueued: z.number(), + }) + .openapi("AmsNotificationsAccepted"); + export const ContributorOpportunitySchema = z .object({ repoFullName: z.string(), diff --git a/src/openapi/spec.ts b/src/openapi/spec.ts index e6995b8842..114c8c6355 100644 --- a/src/openapi/spec.ts +++ b/src/openapi/spec.ts @@ -27,6 +27,7 @@ import { ContributorPrOutcomesSchema, NotificationFeedSchema, NotificationsMarkedSchema, + AmsNotificationsAcceptedSchema, ContributorRewardRiskStrategySchema, ContributorProfileSchema, ContributorScoringProfileSchema, @@ -830,6 +831,52 @@ export function buildOpenApiSpec() { 400: { description: "Invalid mark-read body" }, }, }); + registry.registerPath({ + method: "post", + path: "/v1/contributors/{login}/ams-notifications", + summary: "Enqueue AMS notification events for a contributor", + request: { + params: z.object({ login: z.string() }), + body: { + content: { + "application/json": { + // Inline Zod (not a registered components.schemas $ref) — matches every other request body in + // this generator so Cloudflare schema pruning stays response-free (#write-cloudflare-schema). + schema: z.object({ + events: z + .array( + z.object({ + eventType: z.enum([ + "ams_attempt_started", + "ams_attempt_failed", + "ams_governor_paused", + "ams_pr_outcome", + ]), + repoFullName: z.string(), + pullNumber: z.number().int().min(0), + dedupKey: z.string(), + deeplink: z.string(), + actorLogin: z.string(), + detectedAt: z.string(), + }), + ) + .min(1) + .max(20), + }), + }, + }, + }, + }, + responses: { + 200: { + description: + "Accepts AMS-relevant notification events (attempt start/fail, governor pause, PR outcome) and evaluates them through the existing notify-evaluate → notify-deliver path (#7657).", + content: { "application/json": { schema: AmsNotificationsAcceptedSchema } }, + }, + 400: { description: "Invalid AMS notification body" }, + 403: { description: "Forbidden when login does not match the authenticated session" }, + }, + }); registry.registerPath({ method: "get", path: "/v1/contributors/{login}/repos/{owner}/{repo}/decision", diff --git a/src/queue/job-dispatch.ts b/src/queue/job-dispatch.ts index cc6a9402b2..5b0c499529 100644 --- a/src/queue/job-dispatch.ts +++ b/src/queue/job-dispatch.ts @@ -272,6 +272,8 @@ export async function processJob(env: Env, message: JobMessage): Promise { const deliveries = ( await mapWithConcurrency(events, NOTIFY_EVALUATE_EVENT_CONCURRENCY, (event) => evaluateNotificationEvent(env, event)) ).flat(); + // Same notify-deliver handoff shape as evaluateAndEnqueueNotificationDeliveries (#7657) — keep the + // concurrency-bounded evaluate here for large webhook batches, then enqueue delivers identically. await Promise.all( deliveries.map((delivery) => env.JOBS.send({ diff --git a/src/types.ts b/src/types.ts index 8ec298c3fc..bec5189d99 100644 --- a/src/types.ts +++ b/src/types.ts @@ -189,7 +189,7 @@ export type JobMessage = // that was flooding the maintenance lane with a job per watcher on a popular newly-opened issue. Always // non-empty at enqueue time (see processors.ts); the processor evaluates every event in the batch. type: "notify-evaluate"; - requestedBy: "webhook" | "test"; + requestedBy: "webhook" | "ams" | "test"; events: DetectedNotificationEvent[]; } | { @@ -2384,7 +2384,16 @@ export type DigestSubscriptionRecord = { // unless a row is `paused`). export type NotificationChannel = "badge" | "email"; export type NotificationDeliveryStatus = "pending" | "delivered" | "read" | "suppressed"; -export type NotificationEventType = "pull_request_changes_requested" | "pull_request_merged" | "issue_watch_match"; +export type NotificationEventType = + | "pull_request_changes_requested" + | "pull_request_merged" + | "issue_watch_match" + // AMS-relevant kinds (#7657): attempt lifecycle, governor pause, and the miner's own PR-outcome change. + // Delivered through the same evaluateNotificationEvent → notify-deliver path as the webhook kinds above. + | "ams_attempt_started" + | "ams_attempt_failed" + | "ams_governor_paused" + | "ams_pr_outcome"; /** #699 path B: a miner's standing watch on a repo for new grabbable issues. `labels` ([]=any) filters * which issues notify. The `pullNumber` field of the resulting notification event carries the ISSUE number. */ diff --git a/test/unit/miner-ams-notifications.test.ts b/test/unit/miner-ams-notifications.test.ts new file mode 100644 index 0000000000..2e3087d731 --- /dev/null +++ b/test/unit/miner-ams-notifications.test.ts @@ -0,0 +1,153 @@ +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + buildAmsAttemptFailedPayload, + buildAmsAttemptStartedPayload, + buildAmsGovernorPausedPayload, + buildAmsPrOutcomePayload, + publishAmsNotificationEvents, + scheduleAmsNotificationEvents, +} from "../../packages/loopover-miner/lib/ams-notifications.js"; + +const roots: string[] = []; + +afterEach(() => { + vi.restoreAllMocks(); + for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); +}); + +function writeSessionConfig(loginToken = "session-token"): { env: Record; root: string } { + const root = mkdtempSync(join(tmpdir(), "ams-notifications-config-")); + roots.push(root); + const configPath = join(root, "config.json"); + writeFileSync( + configPath, + JSON.stringify({ + activeProfile: "default", + profiles: { + default: { + apiUrl: "https://api.example.test", + session: { token: loginToken }, + }, + }, + }), + ); + return { env: { LOOPOVER_CONFIG_PATH: configPath }, root }; +} + +describe("ams-notifications (#7657)", () => { + it("builds AMS payloads mirroring hosted DetectedNotificationEvent shape", () => { + expect( + buildAmsAttemptStartedPayload({ + recipientLogin: "Miner", + repoFullName: "acme/widgets", + issueNumber: 2, + attemptId: "a1", + detectedAt: "2026-07-21T00:00:00.000Z", + }), + ).toMatchObject({ + eventType: "ams_attempt_started", + recipientLogin: "miner", + pullNumber: 2, + }); + expect( + buildAmsAttemptFailedPayload({ + recipientLogin: "miner", + repoFullName: "acme/widgets", + issueNumber: 2, + attemptId: "a1", + reason: "abandon", + }).dedupKey, + ).toContain(":abandon"); + expect(buildAmsGovernorPausedPayload({ recipientLogin: "miner", pausedAt: "t" }).pullNumber).toBe(0); + expect( + buildAmsPrOutcomePayload({ + recipientLogin: "miner", + repoFullName: "acme/widgets", + pullNumber: 9, + decision: "closed", + closedAt: "t", + }).eventType, + ).toBe("ams_pr_outcome"); + }); + + it("uses an injected dispatch (job-dispatch evaluate→deliver shape) when provided", async () => { + const dispatch = vi.fn(async () => undefined); + const event = buildAmsGovernorPausedPayload({ recipientLogin: "miner", pausedAt: "t" }); + await expect(publishAmsNotificationEvents([event], { dispatch })).resolves.toEqual({ sent: 1 }); + expect(dispatch).toHaveBeenCalledWith([event]); + }); + + it("returns no_session without a loopover backend session", async () => { + await expect( + publishAmsNotificationEvents([buildAmsGovernorPausedPayload({ recipientLogin: "miner", pausedAt: "t" })], { + env: { LOOPOVER_CONFIG_PATH: join(tmpdir(), "missing-loopover-config.json") }, + }), + ).resolves.toEqual({ sent: 0, error: "no_session" }); + }); + + it("POSTs to the contributor ams-notifications ingest when a session is present", async () => { + const { env } = writeSessionConfig(); + const fetchFn = vi.fn(async () => new Response(JSON.stringify({ accepted: 1 }), { status: 200 })); + const event = buildAmsAttemptStartedPayload({ + recipientLogin: "miner", + repoFullName: "acme/widgets", + issueNumber: 1, + attemptId: "a1", + detectedAt: "2026-07-21T00:00:00.000Z", + }); + await expect(publishAmsNotificationEvents([event], { env, fetchFn })).resolves.toEqual({ sent: 1 }); + expect(fetchFn).toHaveBeenCalledWith( + "https://api.example.test/v1/contributors/miner/ams-notifications", + expect.objectContaining({ + method: "POST", + headers: expect.objectContaining({ authorization: "Bearer session-token" }), + }), + ); + }); + + it("reports http errors and mixed recipients on the HTTP path", async () => { + const { env } = writeSessionConfig(); + const fetchFn = vi.fn(async () => new Response("nope", { status: 500 })); + await expect( + publishAmsNotificationEvents([buildAmsGovernorPausedPayload({ recipientLogin: "miner", pausedAt: "t" })], { + env, + fetchFn, + }), + ).resolves.toEqual({ sent: 0, error: "http_500" }); + + await expect( + publishAmsNotificationEvents( + [ + buildAmsGovernorPausedPayload({ recipientLogin: "a", pausedAt: "t" }), + buildAmsGovernorPausedPayload({ recipientLogin: "b", pausedAt: "t" }), + ], + { env, fetchFn }, + ), + ).resolves.toEqual({ sent: 0, error: "mixed_recipients" }); + }); + + it("scheduleAmsNotificationEvents is fire-and-forget", async () => { + const dispatch = vi.fn(async () => undefined); + scheduleAmsNotificationEvents([buildAmsGovernorPausedPayload({ recipientLogin: "miner", pausedAt: "t" })], { + dispatch, + }); + await vi.waitFor(() => expect(dispatch).toHaveBeenCalled()); + }); + + it("reports dispatch failures without throwing", async () => { + await expect( + publishAmsNotificationEvents([buildAmsGovernorPausedPayload({ recipientLogin: "miner", pausedAt: "t" })], { + dispatch: async () => { + throw new Error("boom"); + }, + }), + ).resolves.toEqual({ sent: 0, error: "boom" }); + }); + + it("returns sent 0 for an empty event list", async () => { + await expect(publishAmsNotificationEvents([])).resolves.toEqual({ sent: 0 }); + }); +}); diff --git a/test/unit/miner-governor-pause-cli.test.ts b/test/unit/miner-governor-pause-cli.test.ts index d481780282..c643189fe3 100644 --- a/test/unit/miner-governor-pause-cli.test.ts +++ b/test/unit/miner-governor-pause-cli.test.ts @@ -92,6 +92,28 @@ describe("loopover-miner governor pause/resume/status CLI (#4851)", () => { expect(governorState.loadPauseState()).toEqual({ paused: false, reason: null, pausedAt: null }); }); + it("publishes an AMS governor-paused notification when a session login is available (#7657)", async () => { + const governorState = tempGovernorState(); + const published: Array<{ eventType: string; recipientLogin: string }> = []; + vi.spyOn(console, "log").mockImplementation(() => undefined); + + expect( + await runGovernorPause(["--reason", "ops", "--json"], { + openGovernorState: () => governorState, + publishAmsNotifications: async (events) => { + published.push(...(events as Array<{ eventType: string; recipientLogin: string }>)); + return { sent: events.length }; + }, + fetchSessionLogin: async () => "miner", + }), + ).toBe(0); + expect(published).toHaveLength(1); + expect(published[0]).toMatchObject({ + eventType: "ams_governor_paused", + recipientLogin: "miner", + }); + }); + it("pauses with no reason and renders the plain-text form", async () => { const governorState = tempGovernorState(); const log = vi.spyOn(console, "log").mockImplementation(() => undefined); diff --git a/test/unit/miner-pr-outcome.test.ts b/test/unit/miner-pr-outcome.test.ts index 5ae4cf293d..e0afe88e9b 100644 --- a/test/unit/miner-pr-outcome.test.ts +++ b/test/unit/miner-pr-outcome.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { MINER_PR_OUTCOME_DECISIONS, MINER_PR_OUTCOME_EVENT, @@ -79,6 +79,21 @@ describe("recordPrOutcomeSnapshot (#4274)", () => { expect(entry.payload).toEqual({ prNumber: 12, decision: "closed", closedAt: "t", reason: "superseded_by_duplicate" }); expect(MINER_PR_OUTCOME_DECISIONS).toEqual(["merged", "closed"]); }); + + it("schedules an AMS pr-outcome notification when recipientLogin is provided (#7657)", () => { + const schedule = vi.fn(); + const ledger = mockLedger(); + recordPrOutcomeSnapshot( + { repoFullName: "acme/widgets", prNumber: 4, decision: "merged", closedAt: "2026-07-21T00:00:00.000Z" }, + { eventLedger: ledger, recipientLogin: "miner", scheduleAmsNotifications: schedule }, + ); + expect(schedule).toHaveBeenCalledOnce(); + expect(schedule.mock.calls[0]![0][0]).toMatchObject({ + eventType: "ams_pr_outcome", + recipientLogin: "miner", + pullNumber: 4, + }); + }); }); describe("readPrOutcomes (#4274)", () => { diff --git a/test/unit/notifications-ams-events.test.ts b/test/unit/notifications-ams-events.test.ts new file mode 100644 index 0000000000..d688faa47a --- /dev/null +++ b/test/unit/notifications-ams-events.test.ts @@ -0,0 +1,136 @@ +import { describe, expect, it } from "vitest"; +import { + buildAmsAttemptFailedEvent, + buildAmsAttemptStartedEvent, + buildAmsGovernorPausedEvent, + buildAmsPrOutcomeEvent, + isAmsNotificationEventType, + normalizeAmsNotificationEventInput, +} from "../../src/notifications/ams-events"; + +describe("AMS notification event builders (#7657)", () => { + it("builds attempt start/fail events with issue-number pullNumber overload", () => { + const started = buildAmsAttemptStartedEvent({ + recipientLogin: "Miner", + repoFullName: "acme/widgets", + issueNumber: 42, + attemptId: "a1", + detectedAt: "2026-07-21T00:00:00.000Z", + }); + expect(started).toMatchObject({ + eventType: "ams_attempt_started", + recipientLogin: "miner", + pullNumber: 42, + dedupKey: "ams_attempt_started:acme/widgets#42:a1", + deeplink: "https://github.com/acme/widgets/issues/42", + }); + + const failed = buildAmsAttemptFailedEvent({ + recipientLogin: "miner", + repoFullName: "acme/widgets", + issueNumber: 42, + attemptId: "a1", + reason: "abandon", + detectedAt: "2026-07-21T00:00:00.000Z", + }); + expect(failed.eventType).toBe("ams_attempt_failed"); + expect(failed.dedupKey).toContain(":abandon"); + }); + + it("builds governor pause and pr-outcome events", () => { + const paused = buildAmsGovernorPausedEvent({ + recipientLogin: "miner", + reason: "ops", + pausedAt: "2026-07-21T00:00:00.000Z", + }); + expect(paused).toMatchObject({ + eventType: "ams_governor_paused", + repoFullName: "ams/governor", + pullNumber: 0, + }); + + const merged = buildAmsPrOutcomeEvent({ + recipientLogin: "miner", + repoFullName: "acme/widgets", + pullNumber: 9, + decision: "merged", + closedAt: "2026-07-21T01:00:00.000Z", + }); + expect(merged.dedupKey).toContain("ams_pr_outcome:merged:"); + expect(merged.deeplink).toContain("/pull/9"); + + const closed = buildAmsPrOutcomeEvent({ + recipientLogin: "miner", + repoFullName: "acme/widgets", + pullNumber: 9, + decision: "closed", + closedAt: null, + detectedAt: "2026-07-21T02:00:00.000Z", + }); + expect(closed.dedupKey).toContain("ams_pr_outcome:closed:"); + }); + + it("normalizes ingest payloads and rejects non-AMS kinds", () => { + expect(isAmsNotificationEventType("ams_attempt_started")).toBe(true); + expect(isAmsNotificationEventType("pull_request_merged")).toBe(false); + + const ok = normalizeAmsNotificationEventInput( + { + eventType: "ams_attempt_started", + repoFullName: "acme/widgets", + pullNumber: 1, + dedupKey: "k", + deeplink: "https://example.com", + actorLogin: "spoofed", + detectedAt: "2026-07-21T00:00:00.000Z", + }, + "Miner", + ); + expect(ok?.recipientLogin).toBe("miner"); + expect(ok?.actorLogin).toBe("miner"); + + expect(normalizeAmsNotificationEventInput(null, "miner")).toBeNull(); + expect( + normalizeAmsNotificationEventInput( + { + eventType: "pull_request_merged", + repoFullName: "acme/widgets", + pullNumber: 1, + dedupKey: "k", + deeplink: "https://example.com", + actorLogin: "miner", + detectedAt: "2026-07-21T00:00:00.000Z", + }, + "miner", + ), + ).toBeNull(); + expect( + normalizeAmsNotificationEventInput( + { + eventType: "ams_attempt_started", + repoFullName: "", + pullNumber: 1, + dedupKey: "k", + deeplink: "https://example.com", + actorLogin: "miner", + detectedAt: "2026-07-21T00:00:00.000Z", + }, + "miner", + ), + ).toBeNull(); + expect( + normalizeAmsNotificationEventInput( + { + eventType: "ams_attempt_started", + repoFullName: "acme/widgets", + pullNumber: -1, + dedupKey: "k", + deeplink: "https://example.com", + actorLogin: "miner", + detectedAt: "2026-07-21T00:00:00.000Z", + }, + "miner", + ), + ).toBeNull(); + }); +}); diff --git a/test/unit/notifications-service.test.ts b/test/unit/notifications-service.test.ts index 9ac0ef9522..3ccbb34c8b 100644 --- a/test/unit/notifications-service.test.ts +++ b/test/unit/notifications-service.test.ts @@ -4,6 +4,7 @@ import { buildNotificationContent, buildNotificationFeed, deliverNotification, + evaluateAndEnqueueNotificationDeliveries, evaluateNotificationEvent, NOTIFICATION_RATE_LIMIT, resolveNotificationChannels, @@ -123,6 +124,80 @@ describe("merged-PR outcome attribution (#702)", () => { }); }); +describe("AMS notification event kinds (#7657)", () => { + it("builds public-safe copy for every AMS event kind (both pr-outcome branches)", () => { + const started = buildNotificationContent(event({ eventType: "ams_attempt_started", pullNumber: 12 })); + expect(started.title.toLowerCase()).toContain("attempt started"); + expect(started.body).toContain("owner/repo#12"); + + const failed = buildNotificationContent(event({ eventType: "ams_attempt_failed", pullNumber: 12 })); + expect(failed.title.toLowerCase()).toContain("attempt failed"); + + const paused = buildNotificationContent( + event({ eventType: "ams_governor_paused", repoFullName: "ams/governor", pullNumber: 0, dedupKey: "ams_governor_paused:miner:t" }), + ); + expect(paused.title.toLowerCase()).toContain("governor paused"); + expect(paused.body.toLowerCase()).toContain("resume"); + + const merged = buildNotificationContent( + event({ + eventType: "ams_pr_outcome", + dedupKey: "ams_pr_outcome:merged:owner/repo#7:2026-05-28T12:00:00.000Z", + }), + ); + expect(merged.title.toLowerCase()).toContain("merge"); + expect(JSON.stringify(merged)).not.toMatch(/reward|payout|trust score|wallet|\$/i); + + const closed = buildNotificationContent( + event({ + eventType: "ams_pr_outcome", + dedupKey: "ams_pr_outcome:closed:owner/repo#7:2026-05-28T12:00:00.000Z", + }), + ); + expect(closed.title.toLowerCase()).toContain("close"); + expect(closed.body.toLowerCase()).toContain("without merge"); + }); + + it("evaluates AMS events and enqueues notify-deliver jobs (job-dispatch handoff shape)", async () => { + const sent: Array<{ type: string; deliveryId?: string; requestedBy?: string }> = []; + const env = createTestEnv({ + JOBS: { + async send(message: { type: string; deliveryId?: string; requestedBy?: string }) { + sent.push(message); + }, + } as unknown as Queue, + }); + const deliveries = await evaluateAndEnqueueNotificationDeliveries(env, [ + event({ + eventType: "ams_attempt_started", + dedupKey: "ams_attempt_started:owner/repo#7:a1", + pullNumber: 7, + }), + event({ + eventType: "ams_governor_paused", + repoFullName: "ams/governor", + pullNumber: 0, + dedupKey: "ams_governor_paused:miner:t1", + }), + ]); + expect(deliveries).toHaveLength(2); + expect(sent).toEqual([ + { type: "notify-deliver", requestedBy: "notify-evaluate", deliveryId: deliveries[0]!.id }, + { type: "notify-deliver", requestedBy: "notify-evaluate", deliveryId: deliveries[1]!.id }, + ]); + }); + + it("returns no deliveries when the badge channel is paused", async () => { + const env = createTestEnv(); + await upsertNotificationSubscription(env, { login: "miner", channel: "badge", status: "paused", source: "test" }); + expect( + await evaluateAndEnqueueNotificationDeliveries(env, [ + event({ eventType: "ams_attempt_failed", dedupKey: "ams_attempt_failed:owner/repo#7:a1" }), + ]), + ).toEqual([]); + }); +}); + describe("evaluateNotificationEvent", () => { it("creates exactly one badge delivery and is idempotent on a duplicate event", async () => { const env = createTestEnv(); diff --git a/test/unit/routes-ams-notifications.test.ts b/test/unit/routes-ams-notifications.test.ts new file mode 100644 index 0000000000..07e8ee026e --- /dev/null +++ b/test/unit/routes-ams-notifications.test.ts @@ -0,0 +1,94 @@ +import { describe, expect, it } from "vitest"; +import { createApp } from "../../src/api/routes"; +import { listNotificationDeliveriesForRecipient } from "../../src/db/repositories"; +import { createTestEnv } from "../helpers/d1"; + +// Operator API token (same posture as routes-notifications.test.ts) — requireContributorAccess allows the +// static `api` identity to post AMS events for any login; session callers need ADMIN_GITHUB_LOGINS first. +const jsonHeaders = (env: Env) => ({ + authorization: `Bearer ${env.LOOPOVER_API_TOKEN}`, + "content-type": "application/json", +}); + +describe("POST /v1/contributors/:login/ams-notifications (#7657)", () => { + it("evaluates AMS events through the existing notify-deliver path", async () => { + const sent: Array<{ type: string; deliveryId?: string; requestedBy?: string }> = []; + const env = createTestEnv({ + JOBS: { + async send(message: { type: string; deliveryId?: string; requestedBy?: string }) { + sent.push(message); + }, + } as unknown as Queue, + }); + const app = createApp(); + + const response = await app.request( + "/v1/contributors/miner/ams-notifications", + { + method: "POST", + headers: jsonHeaders(env), + body: JSON.stringify({ + events: [ + { + eventType: "ams_attempt_started", + repoFullName: "acme/widgets", + pullNumber: 3, + dedupKey: "ams_attempt_started:acme/widgets#3:a1", + deeplink: "https://github.com/acme/widgets/issues/3", + actorLogin: "miner", + detectedAt: "2026-07-21T00:00:00.000Z", + }, + ], + }), + }, + env, + ); + expect(response.status).toBe(200); + const body = (await response.json()) as { accepted: number; enqueued: number; login: string }; + expect(body).toEqual({ login: "miner", accepted: 1, enqueued: 1 }); + expect(sent).toEqual([{ type: "notify-deliver", requestedBy: "notify-evaluate", deliveryId: expect.any(String) }]); + + const deliveries = await listNotificationDeliveriesForRecipient(env, "miner", { eventType: "ams_attempt_started" }); + expect(deliveries).toHaveLength(1); + expect(deliveries[0]).toMatchObject({ eventType: "ams_attempt_started", pullNumber: 3, status: "pending" }); + }); + + it("rejects forged webhook event kinds and invalid bodies", async () => { + const env = createTestEnv(); + const app = createApp(); + + const forged = await app.request( + "/v1/contributors/miner/ams-notifications", + { + method: "POST", + headers: jsonHeaders(env), + body: JSON.stringify({ + events: [ + { + eventType: "pull_request_merged", + repoFullName: "acme/widgets", + pullNumber: 1, + dedupKey: "x", + deeplink: "https://example.com", + actorLogin: "miner", + detectedAt: "2026-07-21T00:00:00.000Z", + }, + ], + }), + }, + env, + ); + expect(forged.status).toBe(400); + + const empty = await app.request( + "/v1/contributors/miner/ams-notifications", + { + method: "POST", + headers: jsonHeaders(env), + body: JSON.stringify({ events: [] }), + }, + env, + ); + expect(empty.status).toBe(400); + }); +});