Merging on Paul's instruction ("roll in any fixes, then proceed with deployment"). This branch fixes the two shared P2s from Astra's K2 reviews (docs #67, sheets #38) once, in the kit: Retry-After becomes a deadline that connectivity wakes can't cross, and the stable-lifetime timer starts at open. The coordinating session read the change. Local make ci passes (62 tests); hub CI is red for infrastructure reasons only. Merged as a merge commit, per AGENTS.md.
K2 follow-up: Retry-After survives wakes; stable streams timed from open open
On branch fix/k2-retry-deadline
Follow-up to #7, cut from main eb1689a. One commit, 67cd5ef. It fixes the two P2s Astra found in both apps' K2 branches (docs #67, sheets #38) once, here, as the coordinator asked.
The bugs
- A wake bypassed Retry-After. The apps passed Retry-After to
Backoff.nextas a floor and then waited withconnectivity.wait, which any wake ends. So foregrounding or a path change cut a server's 30 s short. - Unstable connections reset the backoff. The "stable stream" clock started with the attempt, so a slow refusal, a connect timeout or a 5 s access check counted as stable.
The fix
NativeReconnect(RastrilloApple,@MainActor) has three calls:opened()from the stream'sonOpen,failed(error), andawait pause(). - A.rateLimited(retryAfter:)becomes a not-before deadline, capped at 5 minutes. Only task cancellation ends it. - After the deadline, the pause waits out whatever remains of the backoff withconnectivity.wait. A wake cuts that short and resets the backoff. If the path is down, it waits for the path. -nowis injectable for tests.Backoff.opened(at:)/ended(at:)replaceconnectionEnded(after:). The backoff resets only if the attempt opened and then stayed open longer than 5 s, and each open counts for one attempt only.connectionEndedwas used only by the unmerged Docs and Sheets K2 branches, so removing it breaks no caller on any main.RastrilloApplenow depends onRastrilloNative, forBackoff. Apps already use both.- #7 nit. The README now says the kit string catalog is deferred to K3 and required there (English review, then the package's 12 languages, together with
SyncStatus).
Tests, shown failing first
New tests:
ReconnectTests: -testRetryAfterSurvivesConnectivityWakes: a 1 s Retry-After, with a foreground and two path changes during it, still waits at least 0.95 s. -testRetryAfterThenWaitsForThePath-testStableLifetimeIsTimedFromOpen: with an injected clock, a 10 s attempt that never opened does not reset; open plus 6 s does. -testWakeCutsAnOrdinaryBackoffShortBackoffTests.testAnAttemptThatNeverOpenedIsNeverStable.testResetsOnSuccessAndAfterAStableConnectionOnlymoved to the new API.
Red.
- A stub that reproduces both reported bugs (clock from the attempt's start; the deadline as an interruptible wait) failed the wake test ("A wake crossed the server's Retry-After", 0.16 s), the stable-lifetime test, and the never-opened Backoff test.
- The two tests that guard existing behaviour failed against a second stub, a plain timer that ignores connectivity: "It retried with the path down", and 3.2 s where under 2 s was expected.
Green. make ci passes: swift test ran 62 tests with 0 failures, both companion builds succeeded, and the server and bundle tests passed. Hub CI will be red for the known infrastructure reason (no macOS runner, no unzip for Sparkle).
Docs #67 and Sheets #38 now use NativeReconnect and pin this branch until it merges. Stopping before merge.
Fixes the two shared P2s from Astra's review of docs #67 and sheets #38 once, in the kit: NativeReconnect keeps a Retry-After deadline that connectivity wakes cannot cross, and Backoff times stable streams from open. Also the #7 nit. make ci green locally.