Skip to content

Résilience aux réseaux lents et aux micro-coupures (ADR 0085) - #112

Merged
Lunik merged 6 commits into
masterfrom
claude/offline-network-resilience-wsnm09
Nov 10, 2026
Merged

Lunik merged 6 commits into
masterfrom
claude/offline-network-resilience-wsnm09

Conversation

@Lunik

@Lunik Lunik commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Résumé

Pour jouer dans le train. Dépend de l’ADR 0085 (PR du dépôt Design, même branche), qui remplace partiellement l’ADR 0041.

Point d’appui côté serveur : debuter_commande retrouve une commande déjà traitée avant de contrôler version_etat. Renvoyer la même clé rejoue donc la même réponse, sans double effet.

Technique

  • Client de l’API : 8 s au plus par essai (corps compris), nouveaux essais à 0,5, 1, 2 et 4 s avec une part de hasard, 15 s au plus. Sont renvoyés : coupure, 502, 504, 503 qui ne vient pas de l’API, 429 dont le Retry-After ne dépasse pas 5 s. Jamais un 400, 401, 403 ou 409. Le corps est identique à chaque essai, donc la clé aussi.
  • Une clé par geste (avecCle dans partie.ts), réutilisée par « Réessayer » jusqu’à une réponse du serveur.
  • reseau.ts : état à trois niveaux (en ligne, instable, hors ligne) déduit des requêtes. Une réponse qui tarde plus de 3 s rend le réseau instable ; le passage hors ligne attend 2 s de coupure annoncée par le navigateur ; une sonde /sante guette le retour du réseau (de 2 à 30 s).
  • Resynchronisation au retour du réseau et au retour au premier plan. Une resynchronisation ratée n’efface pas un catalogue déjà lu. Le jeton se renouvelle en arrière-plan, avec une seule ouverture de session à la fois.
  • Préchargement des silhouettes et des jaquettes à l’inactivité (seulement en ligne et sans saveData) ; navigator.storage.persist().
  • API : CompressionLayer (gzip et brotli). Les jetons passent par l’en-tête Authorization, jamais par un cookie : pas d’exposition à BREACH.

Interface

  • La table se prépare hors ligne. « Fusionner », les plis, la boutique, les indices et les récompenses restent inertes.
  • Attente par paliers : après 1 s, les briques se rapprochent (vérifié image par image avec animation-check ; aucun mouvement en mode réduit) ; après 4 s, « Le réseau est lent, nous insistons… », sur la table comme pendant le rituel du pli.
  • Une erreur réseau ne secoue pas la table : icône hors ligne, « Rien n’est perdu », bouton « Réessayer ».
  • Une pastille dans l’en-tête de chaque écran affiche « Réseau instable », « Hors ligne · à jour il y a 4 min », puis « De retour en ligne » pendant 2 s.

Vérification

  • cargo fmt --check, clippy -D warnings, cargo test --workspace (avec un nouveau test de compression) et cargo sqlx prepare --check passent.
  • npm run lint passe (un seul avertissement, qui existait déjà), ainsi que typecheck et npm test (131 tests ; nouveaux : client de l’API, reseau, préchargement, table hors ligne, retour du réseau, « Réessayer » avec la même clé).
  • check_identity : 0 erreur.
  • Recette dans Chromium (mobile et ordinateur) : réseau lent, micro-coupure absorbée (2 envois, même corps), perte du réseau puis « Réessayer », hors ligne, retour en ligne.

Pas de changement de contrat. Une API compressée reste compatible avec les anciennes PWA.


Generated by Claude Code

The full player state comes back with every command; on a slow train
network its size adds latency. Tokens travel in the Authorization header,
never in a cookie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkYb6E3TzRejDQJxsqQ7ix
- Each attempt times out after 8 s; network errors, 502/504, non-API 503
  and short 429 are retried for up to 15 s with the same body, hence the
  same idempotency key, so the server replays instead of double-applying.
- One command key per gesture, reused by the new Retry button.
- Three-level network state (online, unstable, offline) derived from
  requests, with a /sante probe and a 2 s debounce on browser offline.
- Resync on reconnection and when the app comes back to the foreground;
  background token renewal, a single session opening at a time.
- The table can be prepared offline; fusion and economy stay disabled.
- Waiting tiers (bricks drawing closer after 1 s, slow-network notice
  after 4 s), network errors shown without shake, status pill in every
  screen header with last sync time.
- Prefetch catalogue silhouettes and covers when idle; persistent storage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkYb6E3TzRejDQJxsqQ7ix
…ts plan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkYb6E3TzRejDQJxsqQ7ix
…ories

npm audit --audit-level=high started failing on new advisories for
vite 8.3.0-8.3.2 and undici 8.0.0-8.12.1; the lockfile was unchanged by
this branch, so master is affected too. Non-breaking npm audit fix:
vite 8.3.7, undici 8.12.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkYb6E3TzRejDQJxsqQ7ix

Lunik commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

CI rouge sur « PWA (TypeScript) » : l’échec ne vient pas de cette PR. L’étape npm audit --audit-level=high échoue sur des avis publiés récemment pour vite 8.3.0 à 8.3.2 et undici 8.0.0 à 8.12.1. Cette branche ne modifie ni web/package.json ni web/package-lock.json, donc master échoue de la même façon.

Correctif intégré à la PR (784c823) : npm audit fix, sans changement cassant, qui passe à vite 8.3.7 et undici 8.12.4. En local, npm audit --audit-level=high passe, ainsi que le lint, le typecheck, npm test (131 tests) et vite build. Les alertes de gravité moyenne et faible restantes demandent --force (vite-plugin-pwa 1.1.0, changement cassant) : je ne les traite pas ici.


Generated by Claude Code

…EC advisories

cargo audit started failing on advisories published after master's last
run (same findings on master). Semver-compatible updates fix 8 of 9:
ctutils, rustls, rustls-webpki, tokio, webpki-roots, webpki-root-certs,
yoke-derive. libsqlite3-sys (RUSTSEC-2026-0459) remains: it is only in
Cargo.lock through sqlx-sqlite, never compiled, and needs sqlx 0.9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkYb6E3TzRejDQJxsqQ7ix

Lunik commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

CI rouge sur « API et tâches (Rust) » : l’échec ne vient pas de cette PR. L’étape cargo audit échoue sur 9 avis RustSEC publiés récemment, et master donne exactement les mêmes résultats. Les crates ajoutées pour la compression n’en ont aucun.

Correctif intégré (908bee7) : mises à jour compatibles de ctutils, rustls, rustls-webpki, tokio, webpki-roots, webpki-root-certs et yoke-derive, qui corrigent 8 avis sur 9. En local, fmt, clippy, cargo test et sqlx prepare --check passent.

Ce qui bloque encore : RUSTSEC-2026-0459 (libsqlite3-sys 0.30). Cette crate n’est dans Cargo.lock que par sqlx-sqlite, et elle n’est jamais compilée (cargo tree -i libsqlite3-sys --target all est vide). Le correctif demande sqlx 0.9. Deux voies possibles, à trancher par le propriétaire :

  1. l’ajouter à .cargo/audit.toml, comme RUSTSEC-2023-0071 (rsa, même situation) ;
  2. passer à sqlx 0.9, dans une PR à part.

cargo audit restera rouge jusqu’à cette décision.


Generated by Claude Code

@Lunik
Lunik marked this pull request as ready for review November 10, 2026 20:20
…compiled

libsqlite3-sys is only in Cargo.lock through sqlx-sqlite, referenced by
sqlx-macros without being enabled, like rsa (RUSTSEC-2023-0071). The fix
needs sqlx 0.9. Approved by the owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkYb6E3TzRejDQJxsqQ7ix
@Lunik
Lunik merged commit c37caab into master Nov 10, 2026
3 checks passed
@Lunik
Lunik deleted the claude/offline-network-resilience-wsnm09 branch November 10, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants