{\rtf1\ansi\ansicpg1251\cocoartf2868
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fmodern\fcharset0 CourierNewPSMT;\f1\froman\fcharset0 Times-Roman;\f2\fmodern\fcharset0 CourierNewPS-ItalicMT;
}
{\colortbl;\red255\green255\blue255;\red9\green9\blue9;\red255\green255\blue255;\red0\green0\blue0;
\red0\green29\blue164;\red114\green0\blue130;\red17\green109\blue18;}
{\*\expandedcolortbl;;\cssrgb\c3137\c3137\c3137;\cssrgb\c100000\c100000\c100000;\cssrgb\c0\c0\c0;
\cssrgb\c0\c20000\c70196;\cssrgb\c52941\c6275\c58039;\cssrgb\c2353\c49020\c9020;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\li960\fi-960\partightenfactor0

\f0\fs26\fsmilli13333 \cf2 \cb3 \expnd0\expndtw0\kerning0
\outl0\strokewidth0 \strokec2 You are implementing a Jira ticket for the Phoenix Android project. The ticket ID is: $ARGUMENTS
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Follow these steps exactly:
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf5 \strokec5 ## \cf2 \strokec2 Step 1 \'97 Fetch the Jira ticket
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Use `mcp__atlassian__getJiraIssue` with issueIdOrKey = "$ARGUMENTS" (e.g. "PHOEN-2699").
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Read the full description carefully. Note:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Summary / title
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Description (acceptance criteria, requirements, notes)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Any Figma URLs embedded in the description or attachments
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Subtasks or linked issues
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf5 \strokec5 ## \cf2 \strokec2 Step 2 \'97 Fetch Figma designs (if present)
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 If the ticket description contains a figma.com URL:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 1. \cf2 \strokec2 Load the `figma:figma-implement-design` skill first.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 2. \cf2 \strokec2 Extract the fileKey and nodeId from the URL.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 3. \cf2 \strokec2 Call `mcp__plugin_figma_figma__get_design_context` to fetch the design.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 4. \cf2 \strokec2 If the MCP call fails due to a rate limit or quota error, **stop and ask the user**:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 \'a0\'a0\cf5 \strokec5 > \cf2 \strokec2 "Figma MCP is rate-limited. Please attach a screenshot or mockup of the design and I'll continue from that."
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 \'a0\'a0\cf5 \strokec5 - \cf2 \strokec2 Wait for the user to attach an image before proceeding.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 \'a0\'a0\cf5 \strokec5 - \cf2 \strokec2 Use the attached screenshot as the visual spec in place of the MCP design context.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 5. \cf2 \strokec2 Use the design (or screenshot) as the visual spec for your implementation.
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 If no Figma URL is present:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Determine whether the ticket is UI-related by checking the summary and description for keywords such as: screen, UI, layout, design, view, composable, component, button, dialog, bottom sheet, navigation, visual, icon, color, typography, animation, theme.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 If the ticket IS UI-related, **stop and ask the user in the console**:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 \'a0\cf5 \strokec5 > \cf2 \strokec2 "No Figma link was found in the Jira ticket. Do you have a Figma design URL for this ticket? (paste it or press Enter to skip)"
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 \'a0\cf5 \strokec5 - \cf2 \strokec2 If the user pastes a URL: load `figma:figma-implement-design`, then fetch the design as above before continuing.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 \'a0\cf5 \strokec5 - \cf2 \strokec2 If the user skips (empty input): proceed based on the ticket description alone.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 If the ticket is NOT UI-related, proceed based on the ticket description alone.
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf5 \strokec5 ## \cf2 \strokec2 Step 3 \'97 Explore the codebase
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Before writing any code, explore the relevant parts of the codebase:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Identify which module(s) the change belongs to (`app/`, `chat/`, `common/`)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Find related screens, ViewModels, navigation routes, and data sources
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Check existing patterns for the feature area (similar screens, similar flows)
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf5 \strokec5 ## \cf2 \strokec2 Step 4 \'97 Implement
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Follow the project conventions from CLAUDE.md exactly:
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Architecture**
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Three-module clean architecture: `common` \uc0\u8594  `chat` \u8594  `app`
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Layers per module: `ui/` \uc0\u8594  `domain/` \u8594  `data/`
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **UseCase pattern** \'97 extract all business logic out of ViewModels into UseCases:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f2\i \cf6 \strokec6 kotlin
\f1\i0\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 class GetFooUseCase @Inject constructor(
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val fooRepository: FooRepository,
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 ) \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0suspend operator fun invoke(param: Param): Result<Foo> \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0// business logic lives here, not in ViewModel
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 One UseCase per distinct business operation (e.g. `GetFooUseCase`, `SubmitFooUseCase`)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Place in `domain/usecase/` inside the relevant module
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 ViewModels only orchestrate: collect results, map to UiState, emit UiActions
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Never put business logic (validation, mapping, filtering, calculations) directly in a ViewModel
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **ViewModel pattern**
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f2\i \cf6 \strokec6 kotlin
\f1\i0\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 @HiltViewModel
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 class FooViewModel @Inject constructor(
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val getFoo: GetFooUseCase,
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 ) : ViewModel() \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0var uiState by mutableStateOf(FooUiState())
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0private set
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val _uiAction = Channel<FooUiAction>()
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0val uiAction = _uiAction.receiveAsFlow()
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Use `Channel<UiAction>` for one-time events (navigation, snackbars)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Use `mutableStateOf` for observable screen state
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 `viewModelScope.launch \{ \}` \'97 always block body, not `= expr`
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Navigation**
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Routes: sealed class with data object entries
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Params via `Uri.encode(Json.encodeToString(entry))`
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 Composable: `LaunchedEffect(uiAction) \{ ... \}`
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Logging** \'97 use extension functions only, never `Log.d`:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f2\i \cf6 \strokec6 kotlin
\f1\i0\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 logV \{ \} logD \{ \} logI \{ \} logW(err) \{ \} logE(err) \{ \}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Strings** \'97 add new strings to `res/values/strings.xml`
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Not-implemented actions**:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f2\i \cf6 \strokec6 kotlin
\f1\i0\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 snackbar.show(SnackbarEvent.Info(strings.getString(R.string.not_implemented)))
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Code style** \'97 no inline comments unless the WHY is non-obvious. Run ktlintFormat mentally.
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf5 \strokec5 ## \cf2 \strokec2 Step 5 \'97 Write unit tests
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Priority: UseCases must be covered by unit tests.** They contain the business logic and are the most important layer to verify.
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Test structure:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 1. \cf2 \strokec2 **UseCases** \'97 highest priority. Test every branch: happy path, edge cases, error handling. Mock the repository/data source only.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 2. \cf2 \strokec2 **ViewModels** \'97 test state transitions and action emissions triggered by UseCase results. Mock the UseCase.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 3. \cf2 \strokec2 **Domain model transformations** \'97 test pure mapping/transformation functions directly.
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 **Mocking: always use MockK (`io.mockk`). Never use Mockito.**
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 MockK handles Kotlin final classes natively \'97 no `open` modifiers on production classes, no JVM flags.
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf2 \strokec2 Add to module test deps: `testImplementation(libs.io.mockk)`
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 UseCase test pattern:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f2\i \cf6 \strokec6 kotlin
\f1\i0\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 class GetFooUseCaseTest \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val fooRepository: FooRepository = mockk()
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val useCase = GetFooUseCase(fooRepository)
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0@Test
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0fun `given valid param when invoked then returns expected result`() = runTest \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0coEvery \{ fooRepository.getFoo(any()) \} returns ApiResult.Success(fakeFoo)
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0val result = useCase(param)
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0assertThat(result).isInstanceOf(ApiResult.Success::class)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\}
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0@Test
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0fun `given repository error when invoked then propagates failure`() = runTest \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0coEvery \{ fooRepository.getFoo(any()) \} returns ApiResult.Failure(500, "error")
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0val result = useCase(param)
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0assertThat(result).isInstanceOf(ApiResult.Failure::class)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 ViewModel test pattern:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f2\i \cf6 \strokec6 kotlin
\f1\i0\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 @OptIn(ExperimentalCoroutinesApi::class)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 class FooViewModelTest \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val testDispatcher = StandardTestDispatcher()
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val getFoo: GetFooUseCase = mockk()
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0private val resultHandler: ResultHandler = mockk()
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0@Before fun setUp() \{ Dispatchers.setMain(testDispatcher) \}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0@After fun tearDown() \{ Dispatchers.resetMain() \}
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0@Test
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0fun `given successful result when init then state is populated`() = runTest \{
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0coEvery \{ getFoo(any()) \} returns ApiResult.Success(fakeFoo)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0coEvery \{ resultHandler.parseResult<Foo>(any(), any(), any()) \} returns fakeFoo
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0val viewModel = FooViewModel(getFoo, resultHandler)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0advanceUntilIdle()
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\'a0\'a0\'a0\'a0assertThat(viewModel.uiState.foo).isEqualTo(fakeFoo)
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \'a0\'a0\'a0\}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf7 \strokec7 \}
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 ```
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 MockK quick reference:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 `mockk<T>()` \'97 create a mock
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 `coEvery \{ suspendFn(...) \} returns value` \'97 stub a suspend function
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 `coEvery \{ ... \} returnsMany listOf(a, b)` \'97 different value per call
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 - \cf2 \strokec2 `any()` \'97 matches any argument including nullable
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 Use `kotlinx-coroutines-test` (`runTest`, `StandardTestDispatcher`, `advanceUntilIdle`) for coroutine-based tests.
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf5 \strokec5 ## \cf2 \strokec2 Step 6 \'97 Verify
\f1\fs24 \cf0 \strokec4 \
\

\f0\fs26\fsmilli13333 \cf2 \strokec2 After writing all files:
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 1. \cf2 \strokec2 Check for ktlint style issues (spacing, trailing commas, import order).
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 2. \cf2 \strokec2 Confirm Hilt annotations are in place (`@HiltViewModel`, `@Inject`, `@Module`, `@InstallIn`).
\f1\fs24 \cf0 \strokec4 \

\f0\fs26\fsmilli13333 \cf5 \strokec5 3. \cf2 \strokec2 Confirm navigation wiring is complete end-to-end.
\f1\fs24 \cf0 \strokec4 \
\pard\pardeftab720\partightenfactor0

\f0\fs26\fsmilli13333 \cf5 \cb1 \strokec5 4. \cf2 \strokec2 Summarize what was implemented: files created/modified, tests added, any decisions or trade-offs made.}