<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
  <channel>
    <title>Rubric</title>
    <link>https://driches.github.io/SystemDesignInterviews/</link>
    <description>Updates for Rubric, the interviewer's library.</description>
    <language>en</language>
    <item>
      <title>Version 0.2.0</title>
      <pubDate>Thu, 11 Jun 2026 19:00:18 +0000</pubDate>
      <sparkle:version>5</sparkle:version>
      <sparkle:shortVersionString>0.2.0</sparkle:shortVersionString>
      <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>
      <description><![CDATA[<h3>Added</h3>
<ul>
<li><strong>AI request resilience</strong>: transient AI failures no longer kill an in-flight report or interview generation. A new <code>ResilientAIService</code> decorator wraps every provider (Anthropic, OpenAI, Ollama) and retries on 429 rate-limits and 502/503/504 gateway errors with exponential backoff (3 attempts, ±25% jitter, honoring the <code>Retry-After</code> header when Anthropic sends one). Streaming retries are pre-yield only — once any chunk has been emitted to the consumer the partial response is preserved and the error propagates. Typed <code>AIException</code> subtypes (<code>AIRateLimitException</code>, <code>AIServerException</code>, <code>AINetworkException</code>, <code>AITimeoutException</code>, <code>AICancelledException</code>, <code>AIClientException</code>) replace bare <code>DioException</code>s so the UI can render actionable, classified failure copy.</li>
<li><strong>Stop button for in-flight AI calls</strong>: the Generate, Revise, and Report screens each render a Stop affordance while an AI call is running. The cancellation flows through Dio's <code>CancelToken</code> on <code>AIRequest</code>; the writer↔reviewer loops also check the token between turns so they exit cleanly without committing a partial draft. Stopped generations surface a neutral "Generation stopped" snackbar rather than an error banner.</li>
<li><strong>Per-call AI timeouts</strong>: a new <code>AITimeouts</code> class centralises the connect (10s), send (30s), blocking-receive (60s), and streaming-receive (3min) defaults applied to every provider's Dio client. Streaming endpoints override the receive timeout per request via <code>AITimeouts.streamingRequestOptions()</code>. Tuned in one place — not surfaced in Settings.</li>
<li>Branded dark DMG installer background: dark <code>#1A1A1A</code> canvas with a dot-grid texture, red left-edge accent stripe, faint oversized "R" watermark, corner brackets, and "RUBRIC" wordmark. Generated by <code>app/scripts/generate_dmg_background.py</code> (Pillow) and committed to <code>app/assets/dmg/dmg_background.png</code>. Regenerate at any time with <code>make dmg-bg</code>.</li>
<li><strong>Auto-update (macOS)</strong>: the app now checks for new versions on launch and offers a one-click in-place install via <a href="https://sparkle-project.org/">Sparkle</a> through the <a href="https://pub.dev/packages/auto_updater"><code>auto_updater</code></a> plugin. A new <strong>Updates</strong> section in Settings exposes the "Check for updates automatically" toggle, a manual "Check now" button, and the timestamp of the last successful check; a standard <strong>Check for Updates…</strong> item also lives under the application menu. Releases are signed with an EdDSA key in CI and announced through a Sparkle appcast at <a href="https://driches.github.io/SystemDesignInterviews/appcast.xml">/appcast.xml</a>.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><strong>AI failure messaging</strong>: snackbars on the Generate / Revise / Report screens now surface the typed <code>AIException.userMessage</code> (e.g. "AI provider is rate-limited — retry in 7s.", "AI authentication failed. Check your API key in Settings.") instead of a raw stack trace. The fallback "Generation failed: $e" path remains for unclassified errors.</li>
<li><strong>Release entitlements</strong>: the <code>Release.entitlements</code> no longer requests <code>com.apple.security.app-sandbox</code>. Rubric is distributed under Developer ID (not Mac App Store), and the hardened runtime + notarization are unchanged — dropping the sandbox lets Sparkle replace the running bundle without a custom XPC service.</li>
</ul>]]></description>
      <enclosure
        url="https://driches.github.io/SystemDesignInterviews/downloads/Rubric.dmg"
        length="23933493"
        type="application/octet-stream"
        sparkle:edSignature="JgQzkGWCOhdYEIhGXhg7MFbsKTsQP73BjQ0s/idvVTfUeC2zTxAotOtE3oUZygJwlB9Vqsc4+boBEP103H4KDA==" />
    </item>
  </channel>
</rss>
