Repository: pagopa/io-app
Pull request #6977: docs: [IOPID-2990] startup saga analysis 
Author: login: Ladirico
Labels: name: Legacy-IO-A&I
description: Legacy team: IO - Autenticazione e Identità
Created: 2025-05-14T15:36:10Z | Merged: 2025-05-21T08:14:52Z

## Short description
As an A&I team, we analyzed the startup saga and tried to figure out where we need to work to resolve some technical debt and to begin to unbundle some logic. 
We also jumped at the opportunity to make a diagram describing what flows are executed within the startup saga. 





Diff:
diff --git a/ts/features/settings/common/sagas/profile.ts b/ts/features/settings/common/sagas/profile.ts
index cc61fb42223..167c1bdf503 100644
--- a/ts/features/settings/common/sagas/profile.ts
+++ b/ts/features/settings/common/sagas/profile.ts
@@ -383,6 +383,9 @@ function* checkStoreHashedFiscalCode(
   ) {
     // delete current store pin
     yield* call(deletePin);
+    // Refactor: a similar check is also implemented inside the `startupSaga`.
+    // Consider aligning both flows or centralizing this logic to avoid duplicated behaviors.
+    // See related Jira task: https://pagopa.atlassian.net/browse/IOPID-3047
     yield* put(
       differentProfileLoggedIn({
         isNewInstall: checkIsDifferentFiscalCode === undefined
diff --git a/ts/sagas/startup.diagram.README.md b/ts/sagas/startup.diagram.README.md
new file mode 100644
index 00000000000..1471296cfad
--- /dev/null
+++ b/ts/sagas/startup.diagram.README.md
@@ -0,0 +1,91 @@
+```mermaid
+sequenceDiagram
+  participant App
+  participant StartupSaga
+  participant Mixpanel
+  participant System
+  participant Lollipop
+  participant Session
+  participant Profile
+  participant Onboarding
+  participant FeatureWatchers
+  participant Wallet
+
+  App->>StartupSaga: startApplicationInitialization
+
+  %% --- Phase 1: Initial setup ---
+  note over StartupSaga: Phase 1 – Initial setup
+  StartupSaga->>System: Check ingress screen
+  StartupSaga->>Mixpanel: initMixpanel
+  StartupSaga->>System: Clear local notifications
+  StartupSaga->>System: Delete old installation data
+
+  %% --- Phase 2: Security setup (Lollipop key) ---
+  note over StartupSaga: Phase 2 – Security setup (Lollipop key)
+  StartupSaga->>Lollipop: Generate key
+  StartupSaga->>Lollipop: Check if key is supported
+  alt Device not supported
+    StartupSaga-->>App: Exit early
+  end
+
+  %% --- Phase 3: Offline mode check ---
+  note over StartupSaga: Phase 3 – Offline wallet check
+  StartupSaga->>System: Wallet in offline mode check
+  alt Offline mode
+    StartupSaga-->>App: Exit early
+  end
+
+  %% --- Phase 4: Authentication ---
+  note over StartupSaga: Phase 4 – Authentication
+  StartupSaga->>Session: Track keychain failures
+  StartupSaga->>Session: Authenticate user
+  Session-->>StartupSaga: sessionToken
+  StartupSaga->>Lollipop: Get key info
+  StartupSaga->>Session: Validate session token
+  alt Token expired
+    StartupSaga->>Session: Refresh session token
+  end
+
+  %% --- Phase 5: Create backend client ---
+  note over StartupSaga: Phase 5 – Create backend client
+  StartupSaga->>Session: Create backendClient(apiUrlPrefix, sessionToken, keyInfo)
+
+  %% --- Phase 6: Load session and profile ---
+  note over StartupSaga: Phase 6 – Load session and profile
+  StartupSaga->>Profile: Load session info
+  alt Missing tokens (wallet or BPD)
+    StartupSaga->>Profile: Retry loading session info
+    alt Retry failed
+      StartupSaga->>System: Handle startup error
+      StartupSaga-->>App: Exit early
+    end
+  end
+
+  StartupSaga->>Profile: Load user profile
+  Profile-->>StartupSaga: userProfile
+
+  %% --- Phase 7: Start onboarding ---
+  note over StartupSaga: Phase 7 – startupLoadSuccess(ONBOARDING)
+  StartupSaga->>StartupSaga: startupLoadSuccess(ONBOARDING)
+  StartupSaga->>System: Wait for navigator ready
+  StartupSaga->>Onboarding: Start identification (PIN)
+  alt User resets PIN
+    StartupSaga->>StartupSaga: Restart initialization
+    StartupSaga-->>App: Exit
+  end
+
+  StartupSaga->>Onboarding: Set language, check Mixpanel opt-in
+  StartupSaga->>Onboarding: Check fingerprint, email, PIN
+
+  %% --- Phase 8: Feature watchers ---
+  note over StartupSaga: Phase 8 – Feature watchers
+  StartupSaga->>Wallet: watchWalletSaga
+  StartupSaga->>FeatureWatchers: Messages, Services, IDPay, PN, CGN, TrialSystem
+  StartupSaga->>FeatureWatchers: Watch payments with walletToken
+
+  %% --- Phase 9: Completion ---
+  note over StartupSaga: Phase 9 – Completion
+  StartupSaga->>StartupSaga: startupLoadSuccess(AUTHENTICATED)
+  StartupSaga->>App: App ready
+
+```
diff --git a/ts/sagas/startup.ts b/ts/sagas/startup.ts
index 2c5c059da3c..388bde82c36 100644
--- a/ts/sagas/startup.ts
+++ b/ts/sagas/startup.ts
@@ -158,14 +158,24 @@ const warningWaitNavigatorTime = 2000 as Millisecond;
 /**
  * Handles the application startup and the main application logic loop
  */
+/**
+ * The startup saga is triggered in the following scenarios:
+ * - During the root saga initialization
+ * - On logout or expired session
+ * - On FL session refresh
+ * - When accessing the Wallet mini app in offline mode
+ */
 // eslint-disable-next-line sonarjs/cognitive-complexity, complexity
 export function* initializeApplicationSaga(
   startupAction?: ActionType<typeof startApplicationInitialization>
 ): Generator<ReduxSagaEffect, void, any> {
+  // ingress screen
   const isBlockingScreen = yield* select(isBlockingScreenSelector);
   if (isBlockingScreen) {
     return;
   }
+
+  // LV
   const handleSessionExpiration = !!(
     startupAction?.payload && startupAction.payload.handleSessionExpiration
   );
@@ -188,19 +198,26 @@ export function* initializeApplicationSaga(
 
   // remove all local notifications (see function comment)
   yield* call(cancellAllLocalNotifications);
-  yield* call(previousInstallationDataDeleteSaga);
+  yield* call(previousInstallationDataDeleteSaga); // consider to move out of the startup saga
+  /**
+   * Consider moving previousInstallationDataDeleteSuccess inside previousInstallationDataDeleteSaga
+   * TODO: https://pagopa.atlassian.net/browse/IOPID-3038
+   */
   yield* put(previousInstallationDataDeleteSuccess());
 
   // listen for mixpanel enabling events
   yield* takeLatest(setMixpanelEnabled, handleSetMixpanelEnabled);
 
   // clear cached downloads when the logged user changes
-  yield* takeEvery(differentProfileLoggedIn, handleClearAllAttachments);
-
+  yield* takeEvery(differentProfileLoggedIn, handleClearAllAttachments); // Consider using takeLatest here instead
   // Retrieve and listen for notification permissions status changes
   yield* fork(notificationPermissionsListener);
 
-  // Get last logged in Profile from the state
+  /**
+   * Get last logged in Profile from the state
+   * Consider creating separate selectors for email and fiscal code (refer to the related use case below)
+   * TODO: https://pagopa.atlassian.net/browse/IOPID-3039
+   */
   const lastLoggedInProfileState: ReturnType<typeof profileSelector> =
     yield* select(profileSelector);
 
@@ -208,13 +225,23 @@ export function* initializeApplicationSaga(
     ? O.fromNullable(lastLoggedInProfileState.value.is_email_validated)
     : O.none;
 
-  // Watch for profile changes
+  /**
+   * Watch for profile changes
+   * TODO: https://pagopa.atlassian.net/browse/IOPID-3040
+   */
   yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated);
 
   // Reset the profile cached in redux: at each startup we want to load a fresh
   // user profile.
+  // Might be removable: https://github.com/pagopa/io-app/pull/398/files#diff-8a5b2f3967d681b976fe673762bd1061f5b430130c880c1195b76af06362cf31
+  // It was likely used by the old ingress screen to track check progress
+  // If removed, ensure the condition `if (O.isNone(maybeUserProfile))` is preserved,
+  // as it plays a key role in detecting uninitialized profiles.
+  // TODO: https://pagopa.atlassian.net/browse/IOPID-3042
+
   if (!handleSessionExpiration) {
-    yield* put(resetProfileState());
+    yield* put(resetProfileState()); // Consider identifying all scenarios where the profile should be reset (e.g. Wallet offline).
+    // It might be worth consolidating them into a single function
   }
 
   // We need to generate a key in the application startup flow
@@ -222,6 +249,8 @@ export function* initializeApplicationSaga(
   // Here we are blocking the application startup, but we have the
   // the profile loading spinner active.
 
+  // Consider extracting this logic, for example into the root saga
+
   yield* call(generateLollipopKeySaga);
 
   // This saga must retrieve the publicKey by its own,
@@ -305,9 +334,23 @@ export function* initializeApplicationSaga(
     keyInfo
   );
 
+  // The following functions all rely on backendClient
+
   // Watch for requests to logout
   // Since this saga is spawned and not forked
   // it will handle its own cancelation logic.
+
+  // watchLogoutSaga is launched using `spawn` to detach it from the startupSaga:
+  // this ensures it stays alive even if the parent saga is cancelled or restarted
+  // (e.g. on session expiration or when coming back online).
+  //
+  // Introduced in this PR: https://github.com/pagopa/io-app/pull/1417
+  // to ensure the logout listener remains active independently.
+  // It might be moved directly to the rootSaga, as it behaves like a global watcher.
+  // TODO: https://pagopa.atlassian.net/browse/IOPID-3043
+  //
+  // Caution: this saga handles user state cleanup during logout.
+  // Any changes should be made carefully to avoid regressions in session termination flows.
   yield* spawn(watchLogoutSaga, backendClient.logout);
 
   if (zendeskEnabled) {
@@ -353,6 +396,9 @@ export function* initializeApplicationSaga(
     backendClient.deleteUserDataProcessingRequest
   );
 
+  // The logic below relies on the current active session
+  // and is maintained by separate teams
+
   // Start watching for Services actions
   yield* fork(watchServicesSaga, backendClient, sessionToken);
 
@@ -366,7 +412,7 @@ export function* initializeApplicationSaga(
   yield* fork(watchFciSaga, sessionToken, keyInfo);
 
   // whether we asked the user to login again
-  const isSessionRefreshed = previousSessionToken !== sessionToken;
+  const isSessionRefreshed = previousSessionToken !== sessionToken; // Needs further investigation
 
   // Let's see if have to load the session info, either because
   // we don't have one for the current session or because we
@@ -384,6 +430,8 @@ export function* initializeApplicationSaga(
   // we have to load the session information from the backend.
   // In a future refactoring where the checkSession won't get the session tokens
   // anymore, we will need to rethink about this check.
+  // **However**, this refactor depends on the saga startup integer refactor,
+  // so it momentarily does not have a jira ticket assigned
   if (
     O.isNone(maybeSessionInformation) ||
     (O.isSome(maybeSessionInformation) &&
@@ -458,6 +506,9 @@ export function* initializeApplicationSaga(
   // If user logged in with different credentials, but this device still has
   // user data loaded, then delete data keeping current session (user already
   // logged in)
+  // Refactor: this logic might be duplicated with the one that dispatches the `differentProfileLoggedIn` action.
+  // Consider consolidating the logic in one place to ensure consistency and avoid duplication.
+  // See related Jira task: https://pagopa.atlassian.net/browse/IOPID-3047
   if (
     pot.isSome(lastLoggedInProfileState) &&
     lastLoggedInProfileState.value.fiscal_code !== userProfile.fiscal_code
@@ -488,6 +539,7 @@ export function* initializeApplicationSaga(
 
   const watchAbortOnboardingSagaTask = yield* fork(watchAbortOnboardingSaga);
 
+  // start onboarding
   yield* put(startupLoadSuccess(StartupStatusEnum.ONBOARDING));
   if (!handleSessionExpiration) {
     yield* call(waitForMainNavigator);
@@ -550,12 +602,19 @@ export function* initializeApplicationSaga(
   yield* call(checkConfiguredPinSaga);
   yield* call(checkAcknowledgedFingerprintSaga);
 
+  // email validation polling
   yield* fork(watchEmailValidationSaga);
 
   if (!hasPreviousSessionAndPin || userProfile.email === undefined) {
     yield* call(checkAcknowledgedEmailSaga, userProfile);
   }
 
+  /**
+   * if the checks fail (email already taken or email not validated) then the user
+   * is sent back to the page that communicates the problem and from there if starts
+   * the validation flow. If the user wants to validate the email the flow
+   * that triggers polling begins (watchEmailValidationSaga)
+   */
   userProfile = (yield* call(checkEmailSaga)) ?? userProfile;
 
   // Check for both profile notifications permissions (anonymous
@@ -570,6 +629,7 @@ export function* initializeApplicationSaga(
     yield* call(completeOnboardingSaga);
   }
 
+  // finish the onboarding
   // Stop the watchAbortOnboardingSaga
   yield* cancel(watchAbortOnboardingSagaTask);
 
@@ -709,6 +769,8 @@ export function* initializeApplicationSaga(
 /**
  * Wait until the {@link NavigationService} is initialized.
  * The NavigationService is initialized when is called {@link RootContainer} componentDidMount and the ref is set with setTopLevelNavigator
+ * Consider moving this to a dedicated file.
+ * TODO: https://pagopa.atlassian.net/browse/IOPID-3041
  */
 function* waitForNavigatorServiceInitialization() {
   // eslint-disable-next-line functional/no-let
@@ -740,6 +802,9 @@ function* waitForNavigatorServiceInitialization() {
   });
 }
 
+// Consider moving this to a dedicated file
+// TODO: https://pagopa.atlassian.net/browse/IOPID-3041
+
 function* waitForMainNavigator() {
   // eslint-disable-next-line functional/no-let
   let isMainNavReady = yield* call(NavigationService.getIsMainNavigatorReady);


Reviews:
user: login: shadowsheep1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T10:22:43Z
user: login: shadowsheep1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T12:39:37Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T14:29:55Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T14:38:24Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T14:59:43Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T15:01:07Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T15:17:54Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T15:19:02Z
user: login: shadowsheep1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T12:33:58Z
user: login: Ladirico
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T14:17:49Z
user: login: shadowsheep1
body: LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-20T14:49:55Z

Review comments:
diff_hunk: @@ -189,18 +199,25 @@ export function* initializeApplicationSaga(
   // remove all local notifications (see function comment)
   yield* call(cancellAllLocalNotifications);
   yield* call(previousInstallationDataDeleteSaga);
path: ts/sagas/startup.ts
user: login: shadowsheep1
body: Should we mark `previousInstallationDataDeleteSaga` with a comment as a candidate to be moved outside this saga? What do you think?
created_at: 2025-05-19T10:11:41Z
author_association: MEMBER
diff_hunk: @@ -209,19 +226,25 @@ export function* initializeApplicationSaga(
     : O.none;
 
   // Watch for profile changes
-  yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated);
+  yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated); // Verify whether this is still needed or can be removed
 
   // Reset the profile cached in redux: at each startup we want to load a fresh
   // user profile.
+  // Might be removable: https://github.com/pagopa/io-app/pull/398/files#diff-8a5b2f3967d681b976fe673762bd1061f5b430130c880c1195b76af06362cf31
path: ts/sagas/startup.ts
user: login: shadowsheep1
body: You found it! Great job! 🚀🔥
created_at: 2025-05-19T10:14:19Z
author_association: MEMBER
diff_hunk: @@ -209,19 +226,25 @@ export function* initializeApplicationSaga(
     : O.none;
 
   // Watch for profile changes
-  yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated);
+  yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated); // Verify whether this is still needed or can be removed
 
   // Reset the profile cached in redux: at each startup we want to load a fresh
   // user profile.
+  // Might be removable: https://github.com/pagopa/io-app/pull/398/files#diff-8a5b2f3967d681b976fe673762bd1061f5b430130c880c1195b76af06362cf31
path: ts/sagas/startup.ts
user: login: shadowsheep1
body: Should we consider updating the comment to highlight the importance of the `if (O.isNone(maybeUserProfile))` condition (a few lines below), in case we remove the `resetProfileState` part? What do you think?
created_at: 2025-05-19T10:19:08Z
author_association: MEMBER
diff_hunk: @@ -305,9 +328,14 @@ export function* initializeApplicationSaga(
     keyInfo
   );
 
+  // The following functions all rely on backendClient
+
   // Watch for requests to logout
   // Since this saga is spawned and not forked
   // it will handle its own cancelation logic.
+
+  // spawn is used to detach this saga from its parent (startupSaga),
+  // so it keeps running even if the parent is cancelled
path: ts/sagas/startup.ts
user: login: shadowsheep1
body: Did you get a chance to find out the historical reason why we took this approach?
created_at: 2025-05-19T10:21:50Z
author_association: MEMBER
path: ts/sagas/startup.ts
user: login: shadowsheep1
body: This file checks for a "different logged-in user," but we also have `differentProfileLoggedIn`. Should we add a comment here to revisit whether both are still needed? What do you think?
created_at: 2025-05-19T12:39:37Z
author_association: MEMBER
path: ts/sagas/startup.ts
user: login: Ladirico
body: Do you mean that in parts like [this](https://github.com/pagopa/io-app/blob/d7f30409af38acac28773e03839664771c86fe8a/ts/sagas/startup.ts#L497) here we have a similar control and that these controls could _potentially_ be merged?

created_at: 2025-05-19T14:29:54Z
author_association: CONTRIBUTOR
path: ts/sagas/startup.ts
user: login: Ladirico
body: (And maybe even be positioned outside the startup saga?)
created_at: 2025-05-19T14:38:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -305,9 +328,14 @@ export function* initializeApplicationSaga(
     keyInfo
   );
 
+  // The following functions all rely on backendClient
+
   // Watch for requests to logout
   // Since this saga is spawned and not forked
   // it will handle its own cancelation logic.
+
+  // spawn is used to detach this saga from its parent (startupSaga),
+  // so it keeps running even if the parent is cancelled
path: ts/sagas/startup.ts
user: login: Ladirico
body: I've tried, but unfortunately it was a dead end, [I found the PR](https://github.com/pagopa/io-app/pull/1417) where the logic was changed but unfortunately there is no explanation about this task before it was tracked on pivotaltracker which to date responds with a 404 so we have no context about the content of the task. Reading the code it occurs to me that it could be pulled from the stratup saga code
created_at: 2025-05-19T14:59:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -305,9 +328,14 @@ export function* initializeApplicationSaga(
     keyInfo
   );
 
+  // The following functions all rely on backendClient
+
   // Watch for requests to logout
   // Since this saga is spawned and not forked
   // it will handle its own cancelation logic.
+
+  // spawn is used to detach this saga from its parent (startupSaga),
+  // so it keeps running even if the parent is cancelled
path: ts/sagas/startup.ts
user: login: Ladirico
body: However, I think it is appropriate to mark the issue as very sensitive and address it in a single task of analysis and related evidence
created_at: 2025-05-19T15:01:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -189,18 +199,25 @@ export function* initializeApplicationSaga(
   // remove all local notifications (see function comment)
   yield* call(cancellAllLocalNotifications);
   yield* call(previousInstallationDataDeleteSaga);
path: ts/sagas/startup.ts
user: login: Ladirico
body: Yes, i've add the comment! Thanks!
created_at: 2025-05-19T15:17:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -209,19 +226,25 @@ export function* initializeApplicationSaga(
     : O.none;
 
   // Watch for profile changes
-  yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated);
+  yield* fork(watchProfileEmailValidationChangedSaga, lastEmailValidated); // Verify whether this is still needed or can be removed
 
   // Reset the profile cached in redux: at each startup we want to load a fresh
   // user profile.
+  // Might be removable: https://github.com/pagopa/io-app/pull/398/files#diff-8a5b2f3967d681b976fe673762bd1061f5b430130c880c1195b76af06362cf31
path: ts/sagas/startup.ts
user: login: Ladirico
body: Yes! I was analyzing that part, and i've created the ad hoc task and updated the comment with the information you reported
created_at: 2025-05-19T15:19:02Z
author_association: CONTRIBUTOR
path: ts/sagas/startup.ts
user: login: shadowsheep1
body: Exactly. As you can see [here](https://github.com/pagopa/io-app/blob/61a924f65d2a8155af8912f1403137bbb367468e/ts/features/settings/common/sagas/profile.ts#L387), we already detect when a different profile is logged in and dispatch the `differentProfileLoggedIn` action so that the rest of the app can react accordingly.

Currently, the startup saga implements a similar logic but in a different way. Maybe we could extract that part and rely on the differentProfileLoggedIn action instead, to keep things consistent and avoid duplication.

Something to explore, what do you think?
created_at: 2025-05-20T12:33:58Z
author_association: MEMBER
path: ts/sagas/startup.ts
user: login: Ladirico
body: Yes! I think it's a good idea! I've create the [related task](https://pagopa.atlassian.net/browse/IOPID-3047) and add the comments into the code 
created_at: 2025-05-20T14:17:49Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-14T15:36:22Z
body: ## PR Title Validation for conventional commit type

:white_check_mark: **All good!** PR title follows the conventional commit type.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-14T15:36:24Z
body: ## Jira Pull Request Link

This Pull Request refers to Jira issues:
- [IOPID-2990](https://pagopa.atlassian.net/browse/IOPID-2990)


[IOPID-2990]: https://pagopa.atlassian.net/browse/IOPID-2990?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-05-14T15:51:04Z
body: ## [Codecov](https://app.codecov.io/gh/pagopa/io-app/pull/6977?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 57.46%. Comparing base [(`9de2845`)](https://app.codecov.io/gh/pagopa/io-app/commit/9de28454fa51f4cf4a3f04fdba3aaf5f93a7344f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa) to head [(`56bd8a8`)](https://app.codecov.io/gh/pagopa/io-app/commit/56bd8a83c56cdccac133993b42119c1defbf2ca1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa).
> Report is 2 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/pagopa/io-app/pull/6977/graphs/tree.svg?width=650&height=150&src=pr&token=zsurlZdPFW&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa)](https://app.codecov.io/gh/pagopa/io-app/pull/6977?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa)

```diff
@@           Coverage Diff           @@
##           master    #6977   +/-   ##
=======================================
  Coverage   57.46%   57.46%           
=======================================
  Files        1666     1666           
  Lines       34738    34738           
  Branches     7888     7943   +55     
=======================================
  Hits        19961    19961           
+ Misses      14722    14711   -11     
- Partials       55       66   +11     
```

| [Files with missing lines](https://app.codecov.io/gh/pagopa/io-app/pull/6977?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa) | Coverage Δ | |
|---|---|---|
| [ts/features/settings/common/sagas/profile.ts](https://app.codecov.io/gh/pagopa/io-app/pull/6977?src=pr&el=tree&filepath=ts%2Ffeatures%2Fsettings%2Fcommon%2Fsagas%2Fprofile.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa#diff-dHMvZmVhdHVyZXMvc2V0dGluZ3MvY29tbW9uL3NhZ2FzL3Byb2ZpbGUudHM=) | `46.77% <ø> (ø)` | |
| [ts/sagas/startup.ts](https://app.codecov.io/gh/pagopa/io-app/pull/6977?src=pr&el=tree&filepath=ts%2Fsagas%2Fstartup.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa#diff-dHMvc2FnYXMvc3RhcnR1cC50cw==) | `35.51% <100.00%> (ø)` | |

... and [11 files with indirect coverage changes](https://app.codecov.io/gh/pagopa/io-app/pull/6977/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa)

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/pagopa/io-app/pull/6977?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/pagopa/io-app/pull/6977?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa). Last update [9de2845...56bd8a8](https://app.codecov.io/gh/pagopa/io-app/pull/6977?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pagopa).

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE