Skip to content

Fix agent avatar flicker on create by preserving optimistic avatar - #97870

Open
MelvinBot wants to merge 19 commits into
mainfrom
claude-fixAgentAvatarFlicker
Open

MelvinBot wants to merge 19 commits into
mainfrom
claude-fixAgentAvatarFlicker

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Explanation of Change

Follow-up to Expensify/App#95631, which opens the newly-created agent's DM immediately. Reported in issue 97844: the agent's avatar briefly flickers right as the agent is created (Android & iOS app, staging only).

Root cause: On CREATE_AGENT success, createAgent's successData nulled the entire optimistic personal detail for the agent (PERSONAL_DETAILS_LIST[optimisticAccountID] = null). That wiped the agent's avatar for a beat, so the DM header / LHN icon fell back to a default (accountID-hash) avatar via getIconsForParticipants before the real bot avatar returned from the response — a visible flicker. Both avatars render as bundled local SVGs (the render path resolves catalog CDN URLs to local assets via optimizeAvatarSource), so this is a local default→bot SVG swap, not a network fetch.

Fix: On success, clear only the transient isOptimisticPersonalDetail flag instead of nulling the whole detail, so the avatar stays in place and the response overwrites the values without a gap.

AI-run checks (draft — human testing still required): npm test for tests/unit/AgentActionTest.ts (61 passed) and tests/unit/pages/settings/AddAgentPageTest.tsx (19 passed), plus eslint, tsgo typecheck, and oxfmt. The success data … unit test was updated to assert the new behavior.

Fixed Issues

$ #97844
PROPOSAL:

Tests

  1. Launch Expensify App
  2. Log in with valid credentials
  3. Tap Account>Agents>New Agent
  4. Tap "Build custom agent" button
  5. Tap "Create agent" button
  6. Verify that tapping the "Create Agent" button creates the agent without the avatar flickering during the process.
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

Same as #97844

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Co-authored-by: Michał Jasikowski <mjasikowski@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

There's no code fix to push here — every code and test check on this PR is green. The two remaining ❌ are human-action gates, not code failures:

  • checklist — the PR Author Checklist is failing because 0/38 boxes are checked. This just needs the checklist filled out and the boxes ticked (and the Tests/QA sections completed).
  • Check independent approval — fails at the "Verify peer review" step (job). It requires an approval from an independent reviewer; there's nothing in code that can satisfy it.
All code checks passing

test (job 1–8), typecheck, ESLint check, Oxfmt check, Storybook tests, Bun tests, React Compiler Compliance, Compare knip issues against main, spellcheck, and all build jobs are passing. The two *-perf-tests jobs are still pending (comparison jobs, not blocking failures).

So: nothing for me to fix. To unblock the merge, mjasikowski, please fill out the checklist/QA steps and get an independent review approval.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Both failing checks are human-action gates, not code failures — there's nothing for me to fix by pushing code. Every code check (Bun/Jest tests, typecheck, ESLint, Oxfmt, Storybook, perf) is green.

checklist (PR Author Checklist) — Fails because the author checklist is unchecked (0 out of 38 items). This must be filled out by a human author; I can't check these boxes on your behalf. The ### Tests and ### QA Steps sections still contain the template // TODO placeholders and need real steps (or the title needs [No QA]).

Check independent approval — Fails because the PR has no independent review yet (reviewDecision: REVIEW_REQUIRED, 0 reviews). Since I opened the PR, it needs a review + approval from someone other than the author to satisfy this gate. Assign a reviewer and get an approval.

Once the checklist is completed and an independent reviewer approves, both checks will pass. Let me know if you'd like me to change anything in the actual fix.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@mjasikowski

Copy link
Copy Markdown
Contributor

@MelvinBot remove comments from this PR

@MelvinBot

Copy link
Copy Markdown
Contributor Author

⚠️ Something went wrong. This may be a transient issue — try again. If it persists, check status.anthropic.com.

Run details

@mjasikowski

Copy link
Copy Markdown
Contributor

@MelvinBot remove comments from this PR

@mjasikowski
mjasikowski marked this pull request as ready for review August 11, 2026 20:51
@mjasikowski
mjasikowski requested a review from a team as a code owner August 11, 2026 20:51
@melvin-bot
melvin-bot Bot requested review from JmillsExpensify, chuckdries and mjasikowski and removed request for a team August 11, 2026 20:51
@melvin-bot

melvin-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

@chuckdries @mjasikowski One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot removed the request for review from a team August 11, 2026 20:51
@mjasikowski
mjasikowski removed the request for review from JmillsExpensify August 11, 2026 20:51
Comment thread src/libs/actions/Agent.ts Outdated
value: {[optimisticAccountID]: null},
// Clear only the transient optimistic flag rather than nulling the whole detail. Nulling wiped the
// agent's `avatar` the instant CreateAgent succeeded, so for a beat its report/LHN icon fell back to
// a default avatar before the real (bot) avatar returned from the response — the flicker reported in

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ CONSISTENCY-16 (docs)

This comment uses an em dash (—) in its own sentence. Comments should read as plain, natural sentences without stylistic tics like em/en dashes.

Replace the em dash with plain phrasing, e.g.:

// a default avatar before the real (bot) avatar returned from the response. That is the flicker reported in
// issue 97844. Keeping the avatar in place lets the response overwrite the values without that gap.

Reviewed at: 34c9cd3 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

Comment thread tests/unit/AgentActionTest.ts Outdated
const promptRollback = findUpdate(successData, `${ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT}${accountID}`);

expect(getPersonalDetailValue(successData, accountID)).toBeNull();
// The whole detail is no longer nulled — wiping it briefly dropped the agent's avatar, causing a flicker

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ CONSISTENCY-16 (docs)

This comment uses an em dash (—) in its own sentence. Comments should read as plain, natural sentences without stylistic tics like em/en dashes.

Replace the em dash with plain phrasing, e.g.:

// The whole detail is no longer nulled. Wiping it briefly dropped the agent's avatar, causing a flicker
// (issue 97844). Only the transient optimistic flag is cleared so the avatar survives until the response lands.

Reviewed at: 34c9cd3 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

Co-authored-by: Michał Jasikowski <mjasikowski@users.noreply.github.com>
@MelvinBot
MelvinBot requested a review from a team as a code owner August 11, 2026 20:59
@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Removed the code comments added by this PR and pushed to claude-fixAgentAvatarFlicker (594f991).

  • src/libs/actions/Agent.ts — removed the 4-line comment above the PERSONAL_DETAILS_LIST success value
  • tests/unit/AgentActionTest.ts — removed the 2-line comment above the getPersonalDetailValue assertion

No logic changed — only comment lines were deleted. oxfmt and eslint both pass.

@melvin-bot
melvin-bot Bot requested a review from puneetlath August 11, 2026 20:59
@melvin-bot

melvin-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

@puneetlath Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot removed the request for review from a team August 11, 2026 20:59
@MelvinBot

Copy link
Copy Markdown
Contributor Author

All the code checks are now passing — ESLint, typecheck, test (jobs 1–8), all three build variants, Storybook, Bun tests, oxfmt, and the perf tests (baseline-perf-tests / branch-perf-tests / validate-perf-tests) are green.

The one remaining ❌ is Check independent approval, which isn't a code failure and there's nothing for me to push to fix it. It's red because the PR has no independent human approval yet (reviewDecision: REVIEW_REQUIRED — the only "reviews" so far are automated github-actions comments). It'll clear once an independent internal reviewer approves.

So no fix commit is needed here — the PR just needs a reviewer's approval to go fully green.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🚧 mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@mjasikowski

Copy link
Copy Markdown
Contributor

@MelvinBot merge main and resolve conflicts

Resolved conflicts in src/libs/actions/Agent.ts and tests/unit/AgentActionTest.ts.

main moved the optimistic personal-detail and agent-prompt cleanup out of
createAgent()'s successData into replaceOptimisticAgentWithActualAgent, so that
it always runs after the agent settings redirect. That supersedes this branch's
successData tweak (nulling only isOptimisticPersonalDetail instead of the whole
entry), so main's version of successData is taken and the branch's two entries
are dropped.

The branch's actual fix is unaffected: the optimistic personal detail is still
written with a direct Onyx.merge outside the optimisticData rollback machinery,
and AddAgentPage still awaits that merge before navigating.

Co-authored-by: Michał Jasikowski <mjasikowski@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Merged main and resolved the conflicts. Pushed as abe771ab4ae — the PR is mergeable again.

Two conflicts, both in the same spot: main moved the optimistic personal-detail and agent-prompt cleanup out of createAgent()'s successData into replaceOptimisticAgentWithActualAgent, so the cleanup always runs after the agent-settings redirect. That supersedes this branch's successData tweak (nulling only isOptimisticPersonalDetail instead of the whole entry), so I took main's version and dropped the branch's two entries.

Your actual fix is untouched: the optimistic personal detail is still written with a direct Onyx.merge outside the optimisticData rollback machinery, and AddAgentPage still awaits that merge before navigating.

Resolution detail and verification

src/libs/actions/Agent.ts — took main's successData. The branch wanted {[optimisticAccountID]: {isOptimisticPersonalDetail: null}} to stop the whole personal-detail row (and its avatar) being nulled on success; main removes that entry and the prompt entry entirely and does the clearing in replaceOptimisticAgentWithActualAgent instead. Same goal, and main's is strictly the later approach, so keeping the branch's entry would have re-introduced a write that main deliberately removed.

tests/unit/AgentActionTest.ts — kept main's assertions (both entries absent from successData) with the branch's no-arg getOptimisticAccountID() helper, since the optimistic personal detail is no longer in optimisticData for the helper to read.

src/pages/settings/Agents/AddAgentPage.tsx auto-merged cleanly — main's changes there are keyboard/landscape layout, disjoint from the branch's submit handler.

I also checked that nothing in main's new machinery depends on the personal detail being in optimisticData: the ReplaceOptimisticAgentAccountID middleware rewrites queued requests generically across all Onyx data fields, and replaceOptimisticAgentWithActualAgent clears the optimistic key directly.

Verified:

  • npm test on AgentActionTest, AddAgentPageTest, ReplaceOptimisticAgentWithActualAgentTest, ReplaceOptimisticAgentAccountIDTest — 120 passed, 0 failed
  • npm run typecheck — passed
  • npm run lint on the four touched files — clean
  • npm run react-compiler-compliance-check check src/pages/settings/Agents/AddAgentPage.tsx — clean

npm run lint-changed and react-compiler-compliance-check check-changed couldn't resolve a base ref in this workspace, so I ran lint and check against the explicit file list instead.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@mjasikowski

Copy link
Copy Markdown
Contributor

@eVoloshchak can you please check if you can reproduce the flicker in the latest ad hoc build? I can't on both Firefox and Chrome, but you had more luck with that before

@mjasikowski

Copy link
Copy Markdown
Contributor

@MelvinBot merge main resolve conflicts

Resolved a conflict in src/libs/actions/Agent.ts.

main refactored every ONYXKEYS.PERSONAL_DETAILS_LIST write in this file to go
through the new buildPersonalDetailsUpdate() helper from PersonalDetailsUtils.
The helper is semantically identical (MERGE onto the same key), so the conflict
in createAgent()'s optimisticData -- where this branch deleted the personal
detail entry and main reformatted it -- resolves in favour of this branch: the
entry stays deleted so the optimistic avatar isn't rolled back mid-flight.

Carried main's refactor onto the branch's own new line: the direct
Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, ...) is now
Onyx.update([buildPersonalDetailsUpdate(...)]), matching how main converted the
imperative merge in clearAgentError(). Onyx.update() also returns a promise, so
AddAgentPage still awaits the write before navigating.

Updated AgentActionTest accordingly: the optimistic-personal-detail spy now
watches Onyx.update instead of Onyx.merge.

Co-authored-by: Michał Jasikowski <mjasikowski@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Merged main again and resolved the new conflict. Pushed as 3085d5e8b86 — the PR is mergeable.

One conflict this time. main refactored every ONYXKEYS.PERSONAL_DETAILS_LIST write in Agent.ts to go through a new buildPersonalDetailsUpdate() helper. The clash was in createAgent()'s optimisticData: this branch deleted the personal-detail entry, main reformatted it. The helper is semantically identical — a MERGE onto the same key — so I kept the branch's deletion. The optimistic avatar still stays out of the rollback machinery.

I also carried main's refactor onto the branch's own new line: the direct Onyx.merge is now Onyx.update([buildPersonalDetailsUpdate(...)]). It was the only direct personal-details merge left in src after main's sweep, and Onyx.update() returns a promise too, so AddAgentPage still awaits the write before navigating.

Resolution detail and verification

Why take the branch's side: buildPersonalDetailsUpdate() returns {onyxMethod: MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, value} — a pure typing/ergonomics wrapper. main's diff on this file is entirely mechanical: it converts each inline update object to a helper call and nothing else. So there was no new behaviour on main to preserve, and re-adding the entry would have undone the fix.

Following main's convention: main also converted the imperative merge in clearAgentError() to Onyx.update([buildPersonalDetailsUpdate(...)]). After that sweep, the branch's line 68 was the last Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, ...) anywhere in src, so I matched the convention rather than leaving the one holdout.

Test follow-on: AgentActionTest spied on Onyx.merge to capture the optimistic personal detail. It now spies on Onyx.update and reads the matching entry out of the update array. Nothing else in the file touches Onyx.update, so the other suites are unaffected.

Verified:

  • npm test on AgentActionTest, AddAgentPageTest, ReplaceOptimisticAgentWithActualAgentTest, ReplaceOptimisticAgentAccountIDTest — 120 passed, 0 failed
  • npm run typecheck — passed
  • npm run lint on the four touched files — clean
  • npm run react-compiler-compliance-check check src/pages/settings/Agents/AddAgentPage.tsx — clean

@MelvinBot

Copy link
Copy Markdown
Contributor Author

No code fix is needed here — nothing in the diff can make this check pass.

The only failing check is Check independent approval (workflow "Verify peer review", step Verify peer review). Every functional check is green: all 8 Jest shards, Bun tests, ESLint, Oxfmt, typecheck, spellcheck, Storybook, knip, unused styles, React Compiler compliance, and all four native builds. branch-perf-tests is still running.

Why it fails: it's a policy gate, not a code failure. No formal review has ever been submitted on this PR — the API shows zero APPROVED reviews (the only review entries are bot COMMENTED ones), and reviewDecision is REVIEW_REQUIRED. Review is currently requested from eVoloshchak, chuckdries, and mjasikowski.

To clear it: a requested reviewer other than the author needs to submit an approving review.

One thing worth flagging: eVoloshchak's last test report on this PR said the flicker was still reproducible. That was before the later commits (the reconciliation work, the narrow-transition change, its removal, and two main merges today), so it's stale — but a fresh test pass is likely the real thing standing between this PR and that approval, not the check itself.

I made no code changes and pushed nothing.


view run

@mjasikowski

Copy link
Copy Markdown
Contributor

@eVoloshchak bump

@eVoloshchak

Copy link
Copy Markdown
Contributor

@mjasikowski, still reproducible :(

Screen.Recording.2026-09-23.at.15.41.55.mov
Screen.Recording.2026-09-23.at.15.40.15.mov

@github-actions

Copy link
Copy Markdown
Contributor

🚧 mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 No code fix needed — the one failing check is a human review gate.

Check independent approval is the only failure. It runs verify-peer-review and failed with:

approvers: [],
independentApprovers: [],

The PR simply has no approvals yet. It will go green once an independent reviewer approves. Nothing in the diff causes it, so I didn't push anything.

Everything else passed — test (jobs 1–8), Bun tests, ESLint check, typecheck, Oxfmt check, React Compiler Compliance, Storybook tests, spellcheck, and all builds. branch-perf-tests was still running when I checked, and baseline-perf-tests already passed.


view run

@github-actions

Copy link
Copy Markdown
Contributor

🚧 mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

Copy link
Copy Markdown
Contributor

🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
Built from App PR #97870.

Android 🤖 iOS 🍎
⏩ SKIPPED ⏩ ⏩ SKIPPED ⏩
The build for Android was skipped The build for iOS was skipped
Web 🕸️
https://97870.pr-testing.expensify.com
Web

👀 View the workflow run that generated this build 👀

@mjasikowski

Copy link
Copy Markdown
Contributor

@eVoloshchak one last try with the adhoc build please? this time even GPT can't reproduce it

@eVoloshchak

eVoloshchak commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
screen-20260925-133933-1790336328771.mp4
Android: mWeb Chrome
Screen.Recording.2026-09-25.at.12.57.11.mov
iOS: HybridApp
Screen.Recording.2026-09-25.at.13.10.09.mov
iOS: mWeb Safari
Screen.Recording.2026-09-25.at.13.01.13.mov
MacOS: Chrome / Safari
Screen.Recording.2026-09-25.at.12.44.26.mov

@eVoloshchak eVoloshchak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sucess!
LGTM and tests well

@eVoloshchak

Copy link
Copy Markdown
Contributor

@MelvinBot, please fill out the tests section with the following steps

  1. Launch Expensify App
  2. Log in with valid credentials
  3. Tap Account>Agents>New Agent
  4. Tap "Build custom agent" button
  5. Tap "Create agent" buutton
  6. Verify that tapping the "Create Agent" button creates the agent without the avatar flickering during the process.

And mark this as ready for review

@MelvinBot

Copy link
Copy Markdown
Contributor Author

I added your 6 steps to the Tests section, above the existing JS console check. I also corrected "buutton" to "button" in step 5.

The PR is still a draft. My environment blocked the gh pr ready command, so I couldn't mark it ready for review. mjasikowski or anyone with write access can click Ready for review at the bottom of the PR.


view run

@mjasikowski
mjasikowski marked this pull request as ready for review September 25, 2026 14:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4857466423

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

}
const isNarrowLayout = getIsNarrowLayout();

optimisticPersonalDetailPromise.then(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cancel deferred navigation after leaving the form

When the personal-details write is delayed (for example on a busy device) and the user presses the header back button before it resolves, this callback still runs after AddAgentPage has been removed and navigates to the new DM from whichever route is now active. useBeforeRemove only performs draft cleanup and does not cancel the pending callback, so this can unexpectedly pull a user who backed out of the flow into the agent report. Guard the continuation against the page no longer being active, or prevent leaving while this write is pending.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MelvinBot address this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3abc407. If you leave the builder before the personal-detail write resolves, the app no longer opens the DM.

  • The beforeRemove handler now sets hasLeftPageRef (AddAgentPage.tsx:119).
  • The deferred callback checks that ref first. If it's set, the callback clears the avatar draft and skips navigation (AddAgentPage.tsx:155-159). The agent is still created.
  • I added a unit test for the back-out case.

AddAgentPageTest and AgentActionTest pass (81 tests). ESLint, typecheck, the React Compiler check, and oxfmt also pass.


view run

…lves

Co-authored-by: Michał Jasikowski <mjasikowski@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

4 participants