Skip to content

Server-side deployment notifications (deploy succeeded/failed → push), not polling-only #6

Closed

Description

@serge-ivo

Problem

The deploy badge (GitHub Actions latest run) is polled by the console only while it's open. The user wants to be told when a DEPLOY finishes (so they can check the live version) — independent of whether they're watching, and distinct from the agent's reply.

Approach

  • A durable watcher (Cloudflare Workflow or cron) per repo-with-github that detects a run transitioning to completed (success/failure) and fires a notification via the existing notifyUser (in-app + web push), deep-linked to the repo + the run URL.
  • Dedup so one run notifies once; respect the per-device push dedup already in place.
  • Gated: only for repos with a github_repo + the GitHub App installed.

Scope

  • A poll/cron that tracks last-seen run id per repo in D1; on a new completed run, notifyUser("✅ Deployed #N" / "❌ Build failed #N", url=run.html_url).

Acceptance

  • Push a commit → CI runs → when it goes green/red you get ONE notification (even with the console closed) linking to the run.

Activity

  1. serge-ivo commented on Aug 6, 2026

    @serge-ivo
    ContributorAuthor

    Shipped in 11b60ba (migration 0076 applied on deploy).

    A cron sweep (lib/deploy-watch.ts) walks repos with a github_repo, fetches the latest Actions run through the existing shared client, and fires the existing notifyUser (in-app + web push) deep-linked to the run.

    The decision is a pure, separately-tested function because one case is genuinely dangerous: first sight of a repo must never notify. Every repo that already exists has a completed run behind it, so a watcher that notified on whatever it found first would — on the very deploy that shipped this — have pushed every user a notification about a build they ran days ago. It seeds the watermark silently and speaks only for the next run.

    Meeting the acceptance criterion ("ONE notification") needed two things, not one: the watermark, and NOT advancing it on queued/in_progress — otherwise a run gets consumed before it has anything to report.

    Bounded and rotating: 20 repos per sweep, least-recently-checked first, and last_deploy_checked_at is stamped on every check including silent ones. Leaving it unset on a no-op would pin the batch to the same repos and starve the tail. GitHub is rate-limited per installation and this runs on the per-minute cron.

    Registered as a fourth independent waitUntil: it reaches an external API, making it the likeliest of the sweeps to be slow or rate-limited, and it must not be able to stop the trigger sweep or the delivery pump from draining.

    Gating is by github_repo presence; the installation token is used when available (private repos need it, public ones otherwise burn the shared 60/hr cap). Per-device push dedup is unchanged — this rides the existing notifyUser path.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions