<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
  <channel>
    <title>Vercel Pulse</title>
    <link>https://vercelpulse.loxo.hr/appcast.xml</link>
    <description>Vercel Pulse update feed</description>
    <language>en</language>
    <item>
      <title>Vercel Pulse 1.6.0</title>
      <sparkle:version>7</sparkle:version>
      <sparkle:shortVersionString>1.6.0</sparkle:shortVersionString>
      <sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
      <pubDate>Wed, 28 Oct 2026 06:31:00 +0000</pubDate>
      <description><![CDATA[<style>body{font:13px -apple-system,BlinkMacSystemFont,sans-serif;margin:0;padding:4px 2px;color:#1d1d1f;background:transparent}h3{font-size:12px;text-transform:uppercase;letter-spacing:.4px;color:#6e6e73;margin:14px 0 6px}h3:first-child{margin-top:0}ul{margin:0 0 8px;padding-left:18px}li{margin-bottom:4px}p{margin:0 0 8px}code{font-family:ui-monospace,SFMono-Regular,monospace;font-size:12px;background:rgba(0,0,0,.06);padding:1px 4px;border-radius:3px}@media (prefers-color-scheme:dark){body{color:#f5f5f7}h3{color:#98989d}code{background:rgba(255,255,255,.1)}}</style><h3>Added</h3><ul><li><strong>Automatic updates</strong>, via Sparkle. The app checks daily, and Preferences gains an Updates section with a &quot;Check Now&quot; button and a toggle for automatic checks. Pinned project menus get a &quot;Check for Updates…&quot; item. This is the first version that can update itself — 1.5.0 and earlier have to be replaced by hand once.</li><li><code>LICENSE</code> (proprietary) and <code>README.md</code>.</li></ul><h3>Fixed</h3><ul><li><strong>Stale status was shown as if it were current.</strong> A project kept its last known deployment while every poll failed, and the error was only ever surfaced for projects that had no deployment at all — so after a revoked token or a dropped network, the popover went on showing a confident green &quot;Live&quot; nobody had confirmed in an hour. Failing rows now carry a warning and a dimmed status, a &quot;can&#x27;t reach Vercel&quot; banner appears when everything is failing, and pinned menu bar items show the error instead of hiding it behind the last known deployment.</li></ul><h3>Changed</h3><ul><li>Accounts with more than 100 projects now load all of them — the project list follows Vercel&#x27;s pagination cursor instead of stopping at the first page.</li><li>Cloudflare zones are matched to projects by exact base domain. The old rule used a substring test, so a project called &quot;app&quot; claimed the zone &quot;myapp.com&quot;, and whichever project sorted first won.</li><li>The notification permission prompt now appears on the first deployment state change rather than at launch, where it had no context.</li><li><strong>Distribution is now a DMG, not a zip.</strong> Unzipping with Finder strips the stapled notarization ticket, which forces Gatekeeper to phone home on first launch. The DMG keeps the ticket and gives the normal drag-to-Applications install. The zip is now only the transport used to submit to Apple.</li></ul><h3>Internal</h3><ul><li>The release pipeline was rebuilt around the nine-stage process from the Spelko handbook: sign inside-out → notarize + staple the app → build the DMG → sign + notarize + staple that too → Sparkle signature strictly after stapling → appcast → deploy the site last → verify with cache-busting polls.</li><li><code>scripts/sign-release.sh</code> signs Sparkle&#x27;s helpers plain and innermost-first and the app last with entitlements, never <code>--deep</code>, and finishes by running Sparkle&#x27;s <code>Autoupdate</code> to assert the kernel didn&#x27;t kill it (exit 137). That failure mode breaks in-app updates silently and permanently for everyone on the affected version, and nothing else detects it.</li><li><code>scripts/make-appcast.sh</code> refuses to run on an unstapled DMG, since stapling changes the file size and a <code>length</code> measured earlier makes Sparkle reject the download for every user.</li><li><code>scripts/verify-release.sh</code> proves a published release is reachable, polling with <code>Cache-Control: no-cache</code> and checking an older DMG too.</li><li>A test suite (30 tests) covering the pure logic that has broken before: timestamp formatting, build duration, domain selection, zone matching and the stale-status rules. <code>swift test</code>, and <code>release.sh</code> gates on it.</li><li><code>release.sh</code> automates the whole release: checks, version bump, tests, signed and notarized build, appcast regeneration, commit and tag.</li><li>Both scripts prefer a stored notarytool keychain profile over environment variables, so the Apple app-specific password is entered once, ever.</li><li>Sparkle is vendored in <code>Vendor/Sparkle/</code> rather than fetched as a remote package; SwiftPM&#x27;s artifact download hangs on the build machine.</li><li><code>build-app.sh</code> embeds and signs the framework inside-out. <code>--deep</code> signing, which it used before, mis-signs Sparkle&#x27;s XPC services and embedded Updater.app in a way that only shows up when an update is attempted.</li><li>The update feed and DMGs are hosted on Vercel at <code>vercelpulse.loxo.hr</code>, from the static <code>site/</code> directory. <code>make-appcast.sh</code> also rewrites the download page, so the page and the feed can&#x27;t disagree about the current version.</li><li><code>release.sh</code> edits <code>Info.plist</code> in place instead of with <code>PlistBuddy Set</code>, which rewrote the whole file and dropped every comment in it.</li></ul>]]></description>
      <enclosure
        url="https://vercelpulse.loxo.hr/downloads/VercelPulse-1.6.0-7.dmg"
        sparkle:edSignature="+kGKTRIoop+/6vaezB8NPRm7Veq+l/0MwEbOyLOAY9AaUpm5KbyApcA67ufptCYuR+xhpQBs+jSg7FWa7a7TAg==" length="3642530"
        type="application/octet-stream" />
    </item>
  </channel>
</rss>
