mirror of
https://github.com/penpot/penpot.git
synced 2026-09-24 12:56:15 +00:00
Classify save failures as transient or terminal (`transient-error?` over the repo retryable types plus `:invalid-save-response`). Transient failures keep the head commit queued under a new `:retrying` status and resend it with backoff (2s/8s/20s, then terminal): stamp rotation reuses the same `:commit-id`, the in-flight guard prevents double-sends, and episode tokens silence stale timers. One tagged reconnect notice per episode (hidden on save and on terminal failure, silent recovery) plus a `:retrying` save-indicator state; the browser `online` event and new edits resume the episode. Terminal failures keep the exact `:error` path. Covers tasks 4, 6 and 7 with 31 persistence tests; updates the persistence memory. Relates to #11724 AI-assisted-by: muse-spark-1.3-contributor
1.1 KiB
1.1 KiB
Clojure Idioms (verified)
Behaviors confirmed against the language/stdlib — do not re-derive from assumption; a wrong assumption here already cost a review round.
int?is NOT 32-bit-only: true forLong,Integer,Short,Byte(fixed-precision integers). Clojure integer literals areLong, so(int? 5000)is true.integer?is the general integer predicate; prefer it when any integer kind must match,int?only when fixed precision is meant.awaitis acljs.coremacro asserting(:async &env): it fails at compile time outside an^:asynccontext, never silently.- The analyzer reads
:asynconly from the fn name meta and thefnoperator meta; list-level meta is ignored (would make a MetaFn).(fn ^:async [] …)puts the meta on argv (pre/post only, NOT async). - Valid:
(defn ^:async f),(defn- ^:async f),(^:async fn [] …)(the latter is what stockt/asyncgenerates itself). ^:asyncfns never throw synchronously (rejected promises instead);try/catch/finallysupported; continuations are microtasks, timers and RxJS schedulers are macrotasks (FIFO).