Release: develop -> main - #928
Conversation
EN: The buy flow now shows and quotes exact CHF with rappen instead of rounding to whole francs. DE: Der Kauf zeigt und quotet exakte CHF mit Rappen, statt auf ganze Franken zu runden. <details> <summary>Details</summary> Typing 10000 CHF for REALU at 1.37 yields 7299 shares. The field now snaps to 9999.63 (shares times list in Rappen), the quote request keeps that amount, and payment details render two decimal places. `chargedFiatAmount` snaps to Rappen only. Sell still sends an integer share count. </details>
Promote: staging -> develop
EN: Adds a live RealUnit share-token geo-filter table to the handbook. handbook.realunit.app loads GET /v1/country at runtime and does not copy country rows into the repo. Until DFXswiss/backend is deployed, the realunit columns stay empty and the page says so. DE: Ergänzt die Live-Tabelle des RealUnit-Aktientoken-Geo-Filters im Handbook. handbook.realunit.app lädt GET /v1/country zur Laufzeit und kopiert keine Länderzeilen ins Repo. Solange DFXswiss/backend nicht deployed ist, bleiben die realunit-Spalten leer; die Seite weist darauf hin. <details> <summary>Details</summary> Section `#spec-geo` in `docs/handbook/de/index.html`. Client `docs/handbook/de/geo-filter.js` tries same-origin `/v1/country` (nginx proxy in `handbook.nginx.conf`) then `https://api.dfx.swiss/v1/country`. Screenshot count is unchanged. No Dart/UI change, no goldens. The geo-filter columns only appear after the merged backend `CountryDto.realunit` is live on that API. </details>
EN: The geo-filter table now shows German and English country names. CSV, Excel, and PDF downloads export the currently visible rows. Native-script API foreignName is no longer used. DE: Die Geo-Filter-Tabelle zeigt Ländernamen auf Deutsch und Englisch. CSV, Excel und PDF laden die aktuell angezeigten Zeilen herunter. Die native foreignName der API wird nicht mehr verwendet. <details> <summary>Details</summary> Names come from `Intl.DisplayNames` (ISO 3166-1) so Arabic/Japanese/Chinese script in `foreignName` is not shown. Downloads are generated in the browser from the live `GET /v1/country` payload; no country rows are copied into the repo. </details>
…able from state (#934) EN: The buy amount field keeps the typed value instead of being overwritten by the conversion. Quotes still use the Rappen-exact payable. Tests pin currency-switch and quote wiring so that regression cannot return silently. DE: Das Betragsfeld behält die Eingabe statt sie mit dem Umrechnungsergebnis zu überschreiben. Quotes laufen weiter über den rappengenauen Betrag. Tests pinnen Währungswechsel und Quote-Verdrahtung, damit die Regression nicht still zurückkommt. <details> <summary>Details</summary> Since #926 the conversion result was written back into `fiatText`, and the BuyView listener synced it into the amount TextField after every conversion. Editing became impossible: every keystroke or backspace was overwritten with shares × list price 250ms later (prefill 300 → 299.46; deleting digits snapped back to the next whole-share multiple and got stuck). Reported by a customer via RealUnit (only the default amount worked) and reproduced on-device. Fix: - Conversion results land in a new `payableText` only; `fiatText` always keeps the user's own text (typing in the shares field still converts into the fiat field, that path is unchanged output). - All quote requests (initial, listener re-fetch, gate retries in `PaymentActionButton`) use `quoteAmountText`: the Rappen-exact payable when a conversion is live, otherwise the typed amount. The #926 goal (quote and details in Rappen, no whole-franc rounding) stays intact. - The details page keeps rendering the charge echoed by the quote, unchanged. - Tests pin: deleting digits keeps the typed text; currency switch preserves `fiatText`; `getPaymentInfo` is called with the live payable rather than the field text. The exact charge is visible on the confirmation/details page. An optional display line under the field (`payableText` is already in state) is a product choice and is not part of this PR. `flutter analyze` clean on the touched files; `flutter test` of the three buy test files: 31 passed. </details> --------- Co-authored-by: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com>
Promote: staging -> develop
…935) EN: The buy screen shows the Rappen-exact charge under the amount field without overwriting what the user typed. Labelling a draft for handbook flows and later marking it ready no longer starts a second Maestro run. DE: Die Kaufseite zeigt den rappengenauen Betrag unter dem Feld, ohne die Eingabe zu überschreiben. Ein gelabelter Draft startet beim Ready keinen zweiten Maestro-Lauf. <details> <summary>Details</summary> Buy: - `payableText` already holds the conversion result. A read-only line under the amount field renders it when it is non-empty and differs from the typed amount (e.g. field `300`, line `299.46 CHF`). - Comparison is numeric, so `300` and `300.00` stay hidden. Punctuation lives in the widget template, not the ARB value. - Tests pin visibility of `299.46` and hide when payable is empty, string-equal, or numerically equal. - Visual regression: `buy_charged_amount` golden (field `300`, exact charge `299.46 CHF` under it). Baseline generated on the self-hosted runner. CI: - Observed on #934: `tier3:full` started Maestro; `ready_for_review` started a second macos-latest run on the same SHA because the labeled concurrency group is unique. - `ready_for_review` is removed from Tier 3 `pull_request` types. `labeled` starts a run only when the event label is `tier3:full`; `unlabeled` does not start a run. `pull-request.yaml` is unchanged (draft-skip means ready is the first real Analyze/Visual run). </details> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
EN: These tests pin that a EUR buy sends currency EUR and shows the EUR settlement IBAN from the quote, not a leftover CHF IBAN. Production buy code is unchanged: it already forwards the selected currency and renders the IBAN the API returns. The confirm button is not locked; an EUR quote can still be tapped to confirm. DE: Diese Tests stellen sicher, dass ein EUR-Kauf die Währung EUR sendet und die EUR-IBAN aus der Quote zeigt, nicht eine übrig gebliebene CHF-IBAN. Der Produktionscode bleibt unverändert: er leitet die gewählte Währung bereits weiter und zeigt die IBAN der API. Der Bestätigen-Button wird nicht gesperrt; eine EUR-Quote lässt sich weiterhin bestätigen. <details> <summary>Details</summary> Pins the EUR vs CHF buy-quote IBAN contract in existing test files: - Request body for `PUT /v1/realunit/buy` with `Currency.eur` is `currency: EUR` and maps the EUR IBAN from the mock response. - Default/CHF path maps the CHF settlement IBAN. - `RealUnitBuyPaymentInfoDto.fromJson` parses EUR IBAN + EUR currency. - `BuyPaymentInfoCubit` success for EUR keeps the EUR IBAN. - After converter settlement in EUR, `BuyView` refetches the quote with `Currency.eur`. - On an EUR `BuyPaymentInfoSuccess`, `BuyConfirmButton` receives the EUR IBAN (not the CHF leftover). - Tapping confirm on an EUR quote still calls `confirmPayment`. - Payment details (after confirm) render the formatted EUR IBAN and amount-in EUR. IBANs used as distinct fixtures: - CHF: `CH2208307000560946309` → `CH22 0830 7000 5609 4630 9` - EUR: `CH9708307000560946317` → `CH97 0830 7000 5609 4631 7` No production `lib/` changes. </details>
…olden (#939) EN: A 502 with a plain-text body no longer crashes JSON parsing into a FormatException on the buy quote screen. The quote retry button remains; if the API sent no JSON message, only Retry is shown. A visual baseline pins that retry-only state. DE: Ein 502 mit Klartext-Body erscheint auf dem Kauf-Quote-Screen nicht mehr als FormatException. Der Wiederholen-Button bleibt; ohne JSON-Message zeigt die UI nur Wiederholen. Eine visuelle Baseline pinnt diesen Zustand. <details> <summary>Details</summary> Gateway 502 responses with a non-JSON body (e.g. `error code: 502`) were passed through `jsonDecode`, which threw `FormatException`. That string was stored on `BuyPaymentInfoFailure.message` and rendered as the info title. `ApiException.fromBody` now decodes JSON objects via `fromJson` and otherwise yields an empty user-facing message with the HTTP status. The buy payment-info service uses it on error paths. HTTP 502 is mapped like 503 (`priceSourceUnavailable`). The catch-all no longer copies `Object.toString()` into the UI. A golden (`buy_price_source_unavailable_empty`) pins the empty-message retry-only buy screen. The PNG is generated on the self-hosted runner via `golden-regenerate.yaml`, not locally. </details> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
EN: Buying a large CHF amount no longer dies on Retry. The app now shows the quote cap when the order is above inventory, and it reads 105.000 / 90'000 as one hundred five thousand / ninety thousand instead of rejecting the input. DE: Ein grosser CHF-Betrag stirbt nicht mehr am Retry. Die App zeigt die Quote-Obergrenze, wenn der Auftrag über dem Bestand liegt, und liest 105.000 / 90'000 als einhundertfünftausend / neunzigtausend statt die Eingabe zu verwerfen. <details> <summary>Details</summary> Two buy-CTA failures: 1. Quote `AmountTooHigh` / `LimitExceeded` with `maxVolume` used to map to unknown + Retry, which re-quoted the same over-limit amount. 2. CHF/EUR have at most two decimal places, so `105.000` / `90,000` / `90'000` are thousands groups. The parser treated them as invalid (or, on the raw quote API, `105.000` is 105 francs). The field now accepts the apostrophe and the parser keeps the thousands. Screenshots of min/max and other quote outcomes now show the typed amount in the fields, as on the live screen. The max-amount hint still appears only after an over-inventory quote. </details> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
#941) EN: The app no longer infers a default currency from the phone locale. Until the user picks one, the display currency is EUR, then the account currency from GET /v2/user (set from residence on the API: CH/LI → CHF, otherwise EUR). A late apply is dropped if the wallet closed or was replaced. DE: Die App rät die Default-Währung nicht mehr aus der Telefon-Locale. Ohne gespeicherte Wahl gilt EUR, danach die Account-Währung von GET /v2/user (API setzt sie aus dem Wohnsitz: CH/LI → CHF, sonst EUR). Eine späte Apply wird verworfen, wenn die Wallet geschlossen oder ersetzt wurde. <details> <summary>Details</summary> Local prefs still win. `ApplyAccountCurrencyEvent` is ignored when a currency is already stored and is not written to prefs. Wallet open fetches GET /v2/user and applies that currency. Wallet close restores the unset default (EUR). Replacing the open wallet instance clears then re-applies. KYC applies the field after `getUser` only while that same wallet instance is still open. Buy reads the settings currency from context. The quote is cleared when the converter currency changes, and Confirm stays hidden while the success quote currency lags the picker. Dashboard drops in-flight price/chart/history for the previous currency. Sell is unchanged. </details>
Promote: staging -> develop
) EN: The send-process screen waits for its incoming page animation to finish before starting the transfer or opening the result sheet. A fast prepare failure no longer paints over the still-sliding confirm page. The transfer error mapping itself is unchanged. DE: Der Sende-Prozess-Screen wartet, bis die eingehende Seitenanimation fertig ist, bevor Transfer und Ergebnisdialog starten. Ein schneller Prepare-Fehler übermalt nicht mehr die noch rüberschiebende Prüfseite. Die Fehlerzuordnung des Transfers selbst bleibt unverändert. <details> <summary>Details</summary> `SendConfirmPage` still pushes `SendProcessPage` with a `MaterialPageRoute`. `SendProcessCubit.start()` is no longer called from `BlocProvider.create`. A private route gate starts the cubit once the real route animation completes. `ModalRoute.animation` is a `ProxyAnimation` whose parent is `kAlwaysCompleteAnimation` on the first frame of a push. The gate treats that placeholder as unresolved so it does not start during the slide. The result sheet also waits for the same completion. Widget test: mid-slide the preparing label is shown and the failure sheet is not; after the animation the failure sheet is shown. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
EN: Pay, quote load, and sell-bitbox now wait for the incoming route slide to finish before starting work or opening a result sheet. The send-process gate is extracted into a shared widget so the same handling is not duplicated. Fast failures no longer overlay the previous screen mid-slide. DE: Pay, Quote-Laden und Sell-BitBox warten, bis der eingehende Slide fertig ist, bevor Arbeit startet oder ein Ergebnisdialog aufgeht. Der Send-Prozess-Gate liegt in einem gemeinsamen Widget, damit dasselbe Handling nicht dupliziert wird. Schnelle Fehler übermalen nicht mehr die vorige Seite mitten im Slide. <details> <summary>Details</summary> `RouteAnimationGate` ignores the first-frame `kAlwaysCompleteAnimation` placeholder. `onSettled` runs from the context below `BlocProvider`. Result/reconnect sheets still call `waitForIncomingRouteAnimation` as a second wait. Widget tests cover the shared gate and the pay-process 300 ms slide. SellBitbox cubit tests call `start()` explicitly; the constructor no longer auto-starts. </details> Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
EN: If a software-wallet REALU send already broadcast but the receipt wait timed out, the app now treats that as success when the HTTP status is 500 and the error names the tx hash. A 400 or 409 whose JSON body happens to look like a timeout stays a failure. The failure sheet never shows raw viem or exception text for generic errors. The in-progress copy no longer asks the user to confirm in a wallet. DE: Wenn ein REALU-Send mit Software-Wallet schon broadcastet ist und der Receipt-Wait ausläuft, zeigt die App Erfolg nur bei HTTP 500 und einem Fehler, der den tx-Hash nennt. Ein 400 oder 409, dessen JSON-Body wie ein Timeout aussieht, bleibt ein Fehler. Das Fehler-Sheet zeigt bei generic keinen viem- oder Exception-Rohtext. Der Fortschrittstext verlangt keine Wallet-Bestätigung mehr. <details> <summary>Details</summary> - The transfer service maps HTTP 500 plus a receipt-wait timeout phrase (`timed out while waiting for transaction` and `0x` + 64 hex) to `TransferReceiptTimeoutException`. The cubit maps only that type to success. - Body `statusCode` must not override the HTTP status: HTTP 400/409 with a timeout-like JSON body stays `ApiException`. - Generic failures always use localized copy; viem/timeout strings are never shown. - `sendSigning`: EN `Sending the transfer` / DE `Überweisung wird gesendet`. - The signing-state visual baseline was refreshed from the self-hosted Visual Regression runner for that copy. - Retry still reuses the same prepare id. - Tests on the build host: exception surface, transfer service, send cubit, send page — 113 passed. The matching API change is a separate public pull request. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
EN: Send and Pay no longer stack a second amount or quote page when the camera keeps seeing the same QR. Both scanner pages re-arm only after that route pops. A catalog, real-cubit double-capture tests, and a CONTRIBUTING rule make the same mistake a blocking review finding. DE: Senden und Bezahlen stapeln keine zweite Betrags- oder Quote-Seite mehr, wenn die Kamera denselben QR weiter sieht. Beide Scanner-Seiten rüsten erst wieder auf, nachdem diese Route geschlossen wurde. Katalog, Tests mit echtem Cubit und eine CONTRIBUTING-Regel machen denselben Fehler zum blockierenden Review-Fund. <details> <summary>Details</summary> `QrScannerView` forwards every camera frame. The send recipient page and the pay scan page used to `Navigator.push` the next step and `reset()` the cubit in the same listener turn. That dropped the decoded/valid guard, so the next frame pushed a second copy of the same page. Both consumers now use `pushThenRearm`: push first, call `reset` only after the route pops. Invalid-scan snackbars that do not push still reset immediately. Tests: - Catalog `test/helper/scanner_navigation_catalog.dart` plus a discovery self-test that fails if any `QrScannerView(` under `lib/` is missing from the catalog. - Real-cubit widget tests fire two `BarcodeCapture`s and expect the destination `findsOne`; after pop, a third capture is accepted. - Existing mock page tests now assert `reset` is not called until pop. CONTRIBUTING.md has a new **Scanner navigation — CRITICAL** section; `docs/testing.md` has the matching test contract. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
EN: Adds a Tier-1 integration test for the BitBox sell path that starts with too little ETH. The cubit posts `/v1/faucet`, polls `/v1/blockchain/balances`, then signs and broadcasts the unsigned swap and deposit through the DFX API. The faucet response is held behind a Completer so polling cannot start until the faucet call returns. Existing tests that skip the faucet hop are unchanged. No production code change. DE: Ergänzt einen Tier-1-Integrationstest für den BitBox-Sell-Pfad, der mit zu wenig ETH startet. Der Cubit sendet POST `/v1/faucet`, pollt `/v1/blockchain/balances` und signiert dann Swap und Deposit über die DFX-API. Die Faucet-Antwort liegt hinter einem Completer, damit das Polling erst nach Rückkehr des Faucet-Calls startet. Die bestehenden Tests, die den Faucet-Schritt überspringen, bleiben unverändert. Kein Production-Code. <details> <summary>Details</summary> The previous happy path in `test/integration/sell_bitbox_flow_test.dart` set `ethBalance` above `requiredGasEth`, so `POST /v1/faucet` never ran. The new case uses `_info(ethBalance: 0)` and wires the real `DfxFaucetService` and `DfxBlockchainApiService` (plus the already-real sell service) to the same `MockClient`. Sequence: 1. `POST /v1/faucet` is entered; the 200 is held on a Completer 2. a 5s elapse must not call `/v1/blockchain/balances` (timer not installed yet) 3. Completer completes; cubit is `WaitingForEth` 4. after the 5s poll timer, `POST /v1/blockchain/balances` with `blockchain=Ethereum` and ETH asset id 111 5. unsigned swap + deposit, two FakeBitbox signs, two broadcasts, one confirm 6. `SellBitboxSuccess` Unsigned/broadcast/confirm before the faucet POST fail the test. No BitBox hardware and no live node. Local checks on the build host: analyze clean, format unchanged, `flutter test test/integration/sell_bitbox_flow_test.dart` +6 passed. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
EN: This PR reproduces the customer chart bug as goldens, without a product fix. MAX shows the old holdings curve, then a drop to zero. 1W, 1M, 3M and 1J show the empty plot. DE: Dieser PR bildet den Kunden-Chart-Bug als Goldens ab, ohne Produkt-Fix. MAX zeigt die alte Bestandskurve, dann den Abfall auf 0. 1W, 1M, 3M und 1J zeigen die leere Fläche. <details> <summary>Details</summary> Fixtures: non-zero points older than one year (MAX curve), then zeros through now (1W–1J collapse with `minY == maxY == 0`). Same sequence the customer reported. PNGs from the self-hosted visual-regression runner. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
EN: The portfolio development chart disappeared for time windows where every holding is zero. The cubit now keeps a Y-range from 0 so the line stays drawn, and holds the last value before the window so the stroke spans the selected period. A dashboard golden records the all-zero MAX view; the customer-report 1W–1J goldens show that full-width zero line on a 0…10 axis. Unit tests cover an all-zero series, a one-week window of zeros, and carrying a pre-window sample to minX. DE: Die Bestandsentwicklung verschwand in Zeitfenstern, in denen jeder Punkt 0 ist. Der Cubit hält jetzt eine Y-Spanne ab 0, damit die Linie gezeichnet bleibt, und übernimmt den letzten Wert vor dem Fenster, damit der Strich die gewählte Periode ausfüllt. Ein Dashboard-Golden dokumentiert die Null-MAX-Ansicht; die Kunden-Goldens 1W–1J zeigen diese volle Null-Linie auf einer Achse 0…10. Unit-Tests decken eine Null-Serie, ein 1W-Null-Fenster und das Nachziehen eines Werts vor minX ab. <details> <summary>Details</summary> When every visible point is 0, `average * 0.05` was 0, so `minY == maxY == 0` and `LineChart` drew no curve. The cubit now falls back to a 5.0 currency-unit floor so the chart has a Y-range. Holdings cannot go negative: the bottom line is always clamped to 0 (scale 0…10 for an all-zero series), not centered through negative labels. A clipped period (1W/1M/3M/1J) previously started the stroke at the first interior sample. If that sample sat months into the window, the line began mid-chart. The last sample before `minX` is now held at the window edge so the stroke spans the selected period. An empty window (every sample older than the period) stays empty. `dashboard_portfolio_chart_zero.png` is the all-zero MAX view. After #953, the mixed-history 1W/1M/3M/1J goldens pin the full-width zero line on the 0-axis. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
EN: Five synthetic test customers document different buying patterns on the holdings chart. Each history has at least ten trades inside one year; the diagrams are golden baselines, not live accounts. Handbook spec 80 lists every trade and maps six screenshots (MAX for all five, plus 1J after a sell-to-zero). The screenshot count guard moves from 278 to 284. DE: Fünf synthetische Testkunden dokumentieren unterschiedliches Kaufverhalten auf der Bestandsentwicklung. Jede Historie hat mindestens zehn Trades in einem Jahr; die Diagramme sind Golden-Baselines, keine Live-Konten. Handbook-Spec 80 listet alle Trades und sechs Screenshots (MAX für alle fünf, plus 1J nach Verkauf auf 0). Der Screenshot-Count-Guard steigt von 278 auf 284. <details> <summary>Details</summary> Personas: monthly DCA (12×80), lump then top-ups, eight buys then four sells to zero, interleaved mix, rising scale. Fixtures are local-midnight offsets so MAX and 1J stay stable. No addresses or personal data. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
EN: Handbook PRD deploys have been failing because the mail-preview generator now writes 28 HTML files, not 25. This updates the exact-count guard so a push to staging can ship the handbook again, including spec 80. No product UI change. DE: Die Handbook-PRD-Deploys schlagen fehl, weil der Mail-Preview-Generator jetzt 28 HTML-Dateien schreibt, nicht 25. Dieser Guard-Stand lässt den Staging-Push wieder nach PRD, inklusive Spec 80. Keine Produkt-UI-Änderung. <details> <summary>Details</summary> Count is 24 standalone `add()` mails + 3 `pendingTypes` expansions + 1 index = 28. The loop `add()` template is not counted in the 24. Confirmed against the failed deploy log on staging (got 28). The three extra previews come from DFXswiss/backend#5221: `black-squad`, `kyc-payment-data`, `recommendation-confirmation`. The handbook page `#spec-mails` only links to the generated `/mails/` index, so no extra cards. This unblocks #952 (`Promote: staging -> develop`), whose only red check is `deploy-prd / Build and deploy to PRD`. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
…955) EN: Send is gone from the dashboard. The settings wallet-address screen shows the QR, the stock-token hint, and Send. Tapping Send first shows that transfers only reach registered shareholders and update the share register. Tapping Pay first shows that payment is a two-step REALU-to-ZCHF swap usable at OpenCryptoPay locations, because REALU is a security. DE: Send ist vom Dashboard weg. Die Wallet-Adresse in den Einstellungen zeigt den QR, den Hinweis zu den Aktientoken und Senden. Vor dem Senden erscheint der Hinweis: nur an registrierte Aktionäre, das Aktienbuch wird aktualisiert. Vor dem Bezahlen: Zwei-Schritt-Tausch REALU zu ZCHF an OpenCryptoPay-Standorten, weil REALU ein Wertpapier ist. <details> <summary>Details</summary> Dashboard keeps Buy, Sell, and insider-gated Pay. `/send` and `/pay` land on info screens; Continue opens the existing recipient scanner or OpenCryptoPay scanner. The QR address row shrink-wraps so the cluster stays centered. Shareholder enforcement for the transfer itself lives in the companion backend change. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
|
EN: DE: DetailsFlow This repo does not grant workflow re-run or |
…es (#958) EN: Maestro retries XCUITest driver HTTP 500 hangs instead of sitting until the job timeout. Handbook persona goldens keep trade dates on 2026-08-28 so Visual Regression does not go red at midnight. DE: Maestro retried bei XCUITest-Driver-HTTP-500, statt bis zum Job-Timeout zu warten. Die Handbook-Persona-Goldens halten die Trade-Daten auf dem 28.8.2026, damit Visual Regression nicht um Mitternacht rot wird. <details> <summary>Details</summary> The cancelled Maestro check on the release PR is not a required status check. Analyze & Test, Visual Regression, and Coverage Floor Gate already passed on that SHA. `is_driver_hang_or_death` matches `deviceInfo failed, code: 500`. Each `maestro test` is killed after `MAESTRO_ATTEMPT_TIMEOUT_SEC` (default 480) and retried. Handbook persona `_today()` is a fixed date, not `DateTime.now()`. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
|
EN: DE: DetailsRetry helper: #960 (targets staging). A re-run of the failed Release job still uses the tagged Fastfile without the retry; the change applies on the next develop tag after that PR merges. This account cannot re-run GitHub Actions jobs. |
EN: Android deploy on the v1.2.20 tag failed while uploading native symbols: Sentry reported the files were already on the server, then failed assembling libapp.so chunks. This retries that upload three times so a flake does not block the Play Store upload. DE: Der Android-Deploy auf Tag v1.2.20 ist beim Native-Symbol-Upload gescheitert: Sentry meldete die Dateien schon auf dem Server und scheiterte dann am Zusammensetzen der libapp.so-Chunks. Dieser Stand retried den Upload dreimal, damit ein Flake den Play-Store-Upload nicht blockiert. <details> <summary>Details</summary> iOS deploy on the same Release run succeeded. Analyze / Visual on the release SHA were still running at the time of this PR. The Play Store lane never reached `upload_to_play_store` because symbol upload runs first. Re-running the failed Release job still uses the tagged Fastfile without this retry. This change takes effect on the next develop tag after merge. </details> Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
|
EN: DE: DetailsFollow-up: #962 — Play Store upload first, continue when assemble is stuck on already-uploaded files. |
EN: v1.2.21 android-deploy retried Sentry symbol upload three times and still failed: the CLI said the files were already on the server, then assemble of app.so chunks failed. This uploads the Play Store AAB first and continues when that stuck assemble is the only error. DE: v1.2.21 android-deploy hat den Sentry-Symbol-Upload dreimal retried und ist trotzdem gescheitert: die CLI meldete die Dateien schon auf dem Server, danach scheiterte das Assemble der app.so-Chunks. Dieser Stand lädt das AAB zuerst in den Play Store und macht weiter, wenn nur dieses hängende Assemble fehlschlägt. <details> <summary>Details</summary> Missing symbol files still fail the lane immediately. Other Sentry errors still retry three times and then fail. iOS gets the same continue-on-stuck-assemble path. A re-run of the v1.2.21 Release job still uses the tagged Fastfile without this change. It applies on the next develop tag after merge. </details> --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
Promote: staging -> develop
Automatic Release PR
This PR was automatically created after changes were pushed to develop.
Commits: 2 new commit(s)
Checklist