import type { CiWatchStore } from './subscriptions.js'; import { type CiNotifier, type CiReport, type CiStatusSource, type CiWatchSubscription, type FixSessionOffer, type FixSessionStartOutcome, type FixSessionStarter } from './types.js'; export interface CiWatchServiceDeps { readonly source: CiStatusSource; /** * `load` and `save` are the only members CiWatchService calls. Declared as * the whole class, every test double had to be cast through `unknown`, * CiWatchStore has a private field, so no object literal is structurally * assignable to it, and the casts then accepted doubles carrying members the * class does not have. */ readonly store: Pick; /** Delivers the completion notification to the subscription's channel (optional in tests). */ readonly notifier?: CiNotifier | undefined; /** Starts the opt-in fix-session; absent → the trigger is recorded but not started. */ readonly fixSessionStarter?: FixSessionStarter | undefined; /** * The "fix this?" offer for red runs on watches WITHOUT the auto-start * opt-in: acceptance starts the fix-session with the same brief. Absent → * red runs on non-opted-in watches only notify (today's behavior). */ readonly fixSessionOffer?: FixSessionOffer | undefined; /** * Stamps the started fix-session's id onto the accepted offer's RESOLVED * approval record (broker seam) and publishes the update, so the surface * that accepted gets a live in-process handle. Absent → the id is still * delivered via the channel notification only. */ readonly stampFixSession?: ((offerCallId: string, outcome: FixSessionStartOutcome) => Promise) | undefined; readonly now?: (() => number) | undefined; } export interface CreateCiWatchInput { readonly repo: string; readonly ref?: string | undefined; readonly prNumber?: number | undefined; readonly deliveryChannel: string; readonly triggerFixSession?: boolean | undefined; } export interface CiWatchCheckResult { readonly report: CiReport; readonly notified: boolean; readonly notificationId?: string | undefined; readonly fixSessionTriggered: boolean; /** The REAL spawned session's id (attach/resume-resolvable), never a scheduling handle. */ readonly fixSessionId?: string | undefined; /** The honest failure when the auto-start fix-session did not produce an attachable session. */ readonly fixSessionError?: string | undefined; /** A "fix this?" offer was raised through the approval machinery (its acceptance runs async). */ readonly fixSessionOffered?: boolean | undefined; /** The watch was retired: its terminal verdict was delivered, so its job is done. */ readonly retired?: boolean | undefined; } export declare class CiWatchService { private readonly deps; private subscriptions; /** Whole-store writes run one at a time, in call order. See StoreWriteQueue. */ private readonly writes; constructor(deps: CiWatchServiceDeps); /** * Write the subscriptions as they stand at THIS call, after every write * already queued has finished. * * `checkWatch` is the reason this needs ordering rather than luck: it polls * the forge over the network and only writes when that returns, so its write * is requested long before it lands and routinely overlaps whatever an * operator did in the meantime. Unordered, a `deleteWatch` that had already * returned true could be undone by the in-flight check's older snapshot, and * the deleted watch keeps polling and notifying, or, on the retirement path, * a watch that had already delivered its one terminal verdict comes back and * delivers it again. * * The snapshot is taken here, not deferred to write time: every caller mutates * the subscription array and then saves. */ private save; private now; private all; /** One-shot: the per-job CI status report for a repo/ref/PR. */ status(input: { repo: string; ref?: string; prNumber?: number; }): Promise; listWatches(): Promise; createWatch(input: CreateCiWatchInput): Promise; deleteWatch(id: string): Promise; /** * Check one standing watch: poll its per-job status, and if the verdict has * transitioned to a terminal state (passed/failed) since last time, fire the * channel notification, and, when failed AND the subscription opted in, start * a fix-session pre-briefed with the failing jobs' logs. */ checkWatch(id: string): Promise; /** * Deliver the started fix-session's id to the watch's channel so a surface * can open/attach the session. The id is the payload's machine-readable * tail line (sessionId: ), mirroring how surfaces parse other * structured notification lines. Failure to notify never fails the start. */ private notifyFixSessionStarted; /** The failing-jobs brief (names + logs) shared by the auto-start and offer paths. */ private composeFixBrief; } //# sourceMappingURL=service.d.ts.map