Optimize reviewing-changes skill (#6099)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
This commit is contained in:
Patrick Honkonen
2025-11-01 03:28:18 -04:00
committed by GitHub
parent 9c4bd2ee14
commit 8de3a07715
15 changed files with 3564 additions and 89 deletions

View File

@@ -2,19 +2,10 @@ Use the `reviewing-changes` skill to review this pull request.
The PR branch is already checked out in the current working directory.
Provide a comprehensive review including:
CRITICAL: Create inline comments on specific lines for each issue. Do NOT create one large review comment.
- Summary of changes since last review
- Critical issues found (be thorough)
- Suggested improvements (be thorough)
- Good practices observed (be concise - list only the most notable items without elaboration)
- Action items for the author
- Leverage collapsible <details> sections where appropriate for lengthy explanations or code snippets
When reviewing subsequent commits:
- Track status of previously identified issues (fixed/unfixed/reopened)
- Identify NEW problems introduced since last review
- Note if fixes introduced new issues
IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note what was done well without explaining why or praising excessively.
Output Format:
- Create separate inline comment for each specific issue/recommendation
- Summary comment for overall assessment
- Track status of previously identified issues if this is a subsequent review
- Use collapsible <details> sections in inline comments for lengthy code examples

View File

@@ -1,110 +1,169 @@
---
name: reviewing-changes
description: Performs comprehensive code reviews for Bitwarden Android projects, verifying architecture compliance, style guidelines, compilation safety, test coverage, and security requirements. Use when reviewing pull requests, checking commits, analyzing code changes, verifying Bitwarden coding standards, evaluating MVVM patterns, checking Hilt DI usage, reviewing security implementations, or assessing test coverage. Automatically invoked by CI pipeline or manually for interactive code reviews.
version: 2.0.0
description: Comprehensive code reviews for Bitwarden Android. Detects change type (dependency update, bug fix, feature, UI, refactoring, infrastructure) and applies appropriate review depth. Validates MVVM patterns, Hilt DI, security requirements, and test coverage per project standards. Use when reviewing pull requests, checking commits, analyzing code changes, or evaluating architectural compliance.
---
# Reviewing Changes
## Instructions
Follow this process to review code changes for Bitwarden Android:
**IMPORTANT**: Use structured thinking throughout your review process. Plan your analysis in `<thinking>` tags before providing final feedback. This improves accuracy by 40% according to research.
### Step 1: Understand Context
### Step 1: Detect Change Type
Start with high-level assessment of the change's purpose and approach. Read PR/commit descriptions and understand what problem is being solved.
<thinking>
Analyze the changeset systematically:
1. What files were modified? (code vs config vs docs)
2. What is the PR/commit title indicating?
3. Is there new functionality or just modifications?
4. What's the risk level of these changes?
</thinking>
### Step 2: Verify Compliance
Analyze the changeset to determine the primary change type:
Systematically check each area against Bitwarden standards documented in `CLAUDE.md`:
**Detection Rules:**
- **Dependency Update**: Only gradle files changed (`libs.versions.toml`, `build.gradle.kts`) with version number modifications
- **Bug Fix**: PR/commit title contains "fix", "bug", or issue ID; addresses existing broken behavior
- **Feature Addition**: New files, new ViewModels, significant new functionality
- **UI Refinement**: Only UI/Compose files changed, layout/styling focus
- **Refactoring**: Code restructuring without behavior change, pattern improvements
- **Infrastructure**: CI/CD files, Gradle config, build scripts, tooling changes
1. **Architecture**: Follow patterns in `docs/ARCHITECTURE.md`
- MVVM + UDF (ViewModels with `StateFlow`, Compose UI)
- Hilt DI (interface injection, `@HiltViewModel`)
- Repository pattern and proper data flow
If changeset spans multiple types, use the most complex type's checklist.
2. **Style**: Adhere to `docs/STYLE_AND_BEST_PRACTICES.md`
- Naming conventions, code organization, formatting
- Kotlin idioms (immutability, null safety, coroutines)
### Step 2: Load Appropriate Checklist
3. **Compilation**: Analyze for potential build issues
- Import statements and dependencies
- Type safety and null safety
- API compatibility and deprecation warnings
- Resource references and manifest requirements
Based on detected type, read the relevant checklist file:
4. **Testing**: Verify appropriate test coverage
- Unit tests for business logic and utility functions
- Integration tests for complex workflows
- UI tests for user-facing features when applicable
- Test coverage for edge cases and error scenarios
- **Dependency Update** → `checklists/dependency-update.md` (expedited review)
- **Bug Fix** → `checklists/bug-fix.md` (focused review)
- **Feature Addition** → `checklists/feature-addition.md` (comprehensive review)
- **UI Refinement** → `checklists/ui-refinement.md` (design-focused review)
- **Refactoring** → `checklists/refactoring.md` (pattern-focused review)
- **Infrastructure** → `checklists/infrastructure.md` (tooling-focused review)
5. **Security**: Given Bitwarden's security-focused nature
- Proper handling of sensitive data
- Secure storage practices (Android Keystore)
- Authentication and authorization patterns
- Data encryption and decryption flows
- Zero-knowledge architecture preservation
The checklist provides:
- Multi-pass review strategy
- Type-specific focus areas
- What to check and what to skip
- Structured thinking guidance
### Step 3: Document Findings
### Step 3: Execute Review with Structured Thinking
Identify specific violations with `file:line_number` references. Be precise about locations.
<thinking>
Before diving into details:
1. What are the highest-risk areas of this change?
2. Which architectural patterns need verification?
3. What security implications exist?
4. How should I prioritize my findings?
5. What tone is appropriate for this feedback?
</thinking>
### Step 4: Provide Recommendations
Follow the checklist's multi-pass strategy, thinking through each pass systematically.
Give actionable recommendations for improvements. Explain why changes are needed and suggest specific solutions.
### Step 4: Consult Reference Materials As Needed
### Step 5: Flag Critical Issues
Load reference files only when needed for specific questions:
Highlight issues that must be addressed before merge. Distinguish between blockers and suggestions.
- **Issue prioritization** → `reference/priority-framework.md` (Critical vs Suggested vs Optional)
- **Phrasing feedback** → `reference/review-psychology.md` (questions vs commands, I-statements)
- **Architecture questions** → `reference/architectural-patterns.md` (MVVM, Hilt DI, module org, error handling)
- **Security questions (quick reference)** → `reference/security-patterns.md` (common patterns and anti-patterns)
- **Security questions (comprehensive)** → `docs/ARCHITECTURE.md#security` (full zero-knowledge architecture)
- **Testing questions** → `reference/testing-patterns.md` (unit tests, mocking, null safety)
- **UI questions** → `reference/ui-patterns.md` (Compose patterns, theming)
- **Style questions** → `docs/STYLE_AND_BEST_PRACTICES.md`
### Step 6: Acknowledge Quality
### Step 5: Document Findings
Note well-implemented patterns (briefly, without elaboration). Keep positive feedback concise.
<thinking>
Before writing each comment:
1. Is this issue Critical, Important, Suggested, or just Acknowledgment?
2. Should I ask a question or provide direction?
3. What's the rationale I need to explain?
4. What code example would make this actionable?
5. Is there a documentation reference to include?
</thinking>
## Review Anti-Patterns (DO NOT)
**CRITICAL**: Use inline comments on specific lines, NOT a single large review comment.
- Be nitpicky about linter-catchable style issues
- Review without understanding context - ask for clarification first
- Focus only on new code - check surrounding context for issues
- Request changes outside the scope of this changeset
**Inline Comment Rules**:
- Create separate comment for EACH specific issue on the exact line
- Do NOT create one large summary comment with all issues
- Do NOT update existing comments - always create new comments
- This ensures history is retained for other reviewers
## Examples
**Inline Comment Format** (Concise with Collapsible Details):
```
**[Severity]**: [One-line issue description]
### Good Review Format
<details>
<summary>Details and fix</summary>
```markdown
## Summary
This PR adds biometric authentication to the login flow, implementing MVVM pattern with proper state management.
[Code example or specific fix]
## Critical Issues
- `app/login/LoginViewModel.kt:45` - Mutable state exposed; use `StateFlow` instead of `MutableStateFlow`
- `data/auth/BiometricRepository.kt:120` - Missing null safety check on `biometricPrompt` result
[Rationale explaining why]
## Suggested Improvements
- Consider extracting biometric prompt logic to separate use case class
- Add integration tests for biometric failure scenarios
- `app/login/LoginScreen.kt:89` - Consider using existing `BitwardenButton` component
## Good Practices
- Proper Hilt DI usage throughout
- Comprehensive unit test coverage
- Clear separation of concerns
## Action Items
1. Fix mutable state exposure in `LoginViewModel`
2. Add null safety check in `BiometricRepository`
3. Consider adding integration tests for error flows
Reference: [docs link if applicable]
</details>
```
### What to Focus On
**Example inline comment**:
```
**CRITICAL**: Exposes mutable state
**DO focus on:**
- Architecture violations (incorrect patterns)
- Security issues (data handling, encryption)
- Missing tests for critical paths
- Compilation risks (type safety, null safety)
<details>
<summary>Details and fix</summary>
**DON'T focus on:**
- Minor formatting (handled by linters)
- Personal preferences without architectural basis
- Issues outside the changeset scope
Change `MutableStateFlow<State>` to `StateFlow<State>`:
\```kotlin
private val _state = MutableStateFlow<State>()
val state: StateFlow<State> = _state.asStateFlow()
\```
Exposing MutableStateFlow allows external mutation, violating MVVM unidirectional data flow.
Reference: docs/ARCHITECTURE.md#mvvm-pattern
</details>
```
**Summary Comment Format** (Minimal - Avoid Duplication):
```
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue]
See inline comments for all issue details.
```
**Output Format Notes**:
**What to Include:**
- **Inline comments**: Create separate comment for EACH specific issue with full details in `<details>` tag
- **Summary comment**: Overall assessment (APPROVE/REQUEST CHANGES) + list of CRITICAL issues only
- **Severity levels**: Use CRITICAL (blocking), IMPORTANT (should fix), SUGGESTED (nice to have), or ACKNOWLEDGED (good practice observed)
**What to Exclude:**
- **No duplication**: Never repeat inline comment details in the summary
- **No Important/Suggested in summary**: Only CRITICAL blocking issues belong in summary
- **No "Good Practices" section**: Acknowledge good practices in inline comments if relevant
- **No "Action Items" section**: This duplicates inline comments - avoid entirely
- **No verbose analysis**: Keep detailed analysis (compilation status, security review, rollback plans) in inline comments only
**Visibility Guidelines:**
- **Inline comments visible**: Severity + one-line description only
- **Inline comments collapsed**: Code examples, rationale, references in `<details>` tag
- **Summary visible**: Verdict + critical issues list only
See `examples/review-outputs.md` for complete examples.
## Core Principles
- **Appropriate depth**: Match review rigor to change complexity and risk
- **Specific references**: Always use `file:line_number` format for precise location
- **Actionable feedback**: Say what to do and why, not just what's wrong
- **Constructive tone**: Ask questions for design decisions, explain rationale, focus on code not people
- **Efficient reviews**: Use multi-pass strategy, time-box reviews, skip what's not relevant

View File

@@ -0,0 +1,164 @@
# Bug Fix Review Checklist
## Multi-Pass Strategy
### First Pass: Understand the Bug
<thinking>
Before evaluating the fix:
1. What was the original bug/broken behavior?
2. What is the expected correct behavior?
3. What was the root cause?
4. How was the bug discovered? (user report, test, production)
5. What's the severity? (crash, data loss, UI glitch, minor annoyance)
</thinking>
**1. Understand root cause:**
- What was the broken behavior?
- What caused it?
- How does this fix address the root cause?
**2. Assess scope:**
- How many files changed?
- Is this a targeted fix or broader refactoring?
- Does this affect multiple features?
**3. Check for side effects:**
- Could this break other features?
- Are there edge cases not considered?
### Second Pass: Verify the Fix
<thinking>
Evaluate the fix systematically:
1. Does this fix address the root cause or just symptoms?
2. Are there edge cases not covered?
3. Could this break other functionality?
4. Is the fix localized or does it ripple through the codebase?
5. How do we prevent this bug from returning?
</thinking>
**4. Code changes:**
- Does the fix make sense?
- Is it the simplest solution?
- Any unnecessary changes included?
**5. Testing:**
- Is there a regression test?
- Does test verify the bug is fixed?
- Are edge cases covered?
**6. Related code:**
- Same pattern in other places that might have same bug?
- Should other similar code be fixed too?
## What to CHECK
**Root Cause Analysis**
- Does the fix address the root cause or just symptoms?
- Is the explanation in PR/commit clear?
**Regression Testing**
- Is there a new test that would fail without this fix?
- Does test cover the reported bug scenario?
- Are related edge cases tested?
**Side Effects**
- Could this break existing functionality?
- Are there similar code paths that need checking?
- Does this change behavior in unexpected ways?
**Fix Scope**
- Is the fix appropriately scoped (not too broad, not too narrow)?
- Are all instances of the bug fixed?
- Any related bugs discovered during investigation?
## What to SKIP
**Full Architecture Review** - Unless fix reveals architectural problems
**Comprehensive Testing Review** - Focus on regression tests, not entire test suite
**Major Refactoring Suggestions** - Unless directly related to preventing similar bugs
## Red Flags
🚩 **No test for the bug** - How will we prevent regression?
🚩 **Fix doesn't match root cause** - Is this fixing symptoms?
🚩 **Broad changes beyond the bug** - Should this be split into separate PRs?
🚩 **Similar patterns elsewhere** - Should those be fixed too?
## Key Questions to Ask
Use `reference/review-psychology.md` for phrasing:
- "Can we add a test that would fail without this fix?"
- "I see this pattern in [other file] - does it have the same issue?"
- "Is this fixing the root cause or masking the symptom?"
- "Could this change affect [related feature]?"
## Prioritizing Findings
Use `reference/priority-framework.md` to classify findings as Critical/Important/Suggested/Optional.
## Output Format
Follow the format guidance from `SKILL.md` Step 5 (concise summary with critical issues only, detailed inline comments with `<details>` tags).
```markdown
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue with file:line reference]
See inline comments for all issue details.
```
## Example Review
```markdown
**Overall Assessment:** APPROVE
See inline comments for suggested improvements.
```
**Inline comment examples:**
```
**data/auth/BiometricRepository.kt:120** - SUGGESTED: Extract null handling
<details>
<summary>Details</summary>
Root cause analysis: BiometricPrompt result was nullable but code assumed non-null, causing crash on cancellation (PM-12345).
Consider extracting null handling pattern:
\```kotlin
private fun handleBiometricResult(result: BiometricPrompt.AuthenticationResult?): AuthResult {
return result?.let { AuthResult.Success(it) } ?: AuthResult.Cancelled
}
\```
This pattern could be reused if we add other biometric auth points.
</details>
```
```
**app/auth/BiometricViewModel.kt:89** - SUGGESTED: Add regression test
<details>
<summary>Details</summary>
Add test for cancellation scenario to prevent regression:
\```kotlin
@Test
fun `when biometric cancelled then returns cancelled state`() = runTest {
coEvery { repository.authenticate() } returns Result.failure(CancelledException())
viewModel.onBiometricAuth()
assertEquals(AuthState.Cancelled, viewModel.state.value)
}
\```
This prevents regression of the bug just fixed.
</details>
```

View File

@@ -0,0 +1,166 @@
# Dependency Update Review Checklist
## Multi-Pass Strategy
### First Pass: Identify and Assess
<thinking>
Before diving into details:
1. Which dependencies were updated?
2. What are the version changes? (patch, minor, major)
3. Are any security-sensitive libraries involved? (crypto, auth, networking)
4. Any pre-release versions (alpha, beta, RC)?
5. What's the blast radius if something breaks?
</thinking>
**1. Identify the change:**
- Which library? Old version → New version?
- Major (X.0.0), Minor (0.X.0), or Patch (0.0.X) version change?
- Single dependency or multiple?
**2. Check compilation safety:**
- Any imports in codebase that might break?
- Any deprecated APIs we're currently using?
- Check if this is a breaking change version
### Second Pass: Deep Analysis
<thinking>
For each dependency update:
1. What changes are in this release?
2. Are there breaking changes?
3. Are there security fixes?
4. Do we use the affected APIs?
5. How does this affect our codebase?
</thinking>
**3. Review release notes** (if available):
- Breaking changes mentioned?
- Security fixes included?
- New features we should know about?
- Deprecations that affect our usage?
**4. Verify consistency:**
- If updating androidx library, are related libraries updated consistently?
- BOM (Bill of Materials) consistency if applicable?
- Test dependencies updated alongside main dependencies?
## What to CHECK
**Compilation Safety**
- Look for API deprecations in our codebase
- Check if import statements still valid
- Major version bumps require extra scrutiny
- Beta/alpha versions need stability assessment
**Security Implications** (if applicable)
- Security-related libraries (crypto, auth, networking)?
- Check for CVEs addressed in release notes
- Review security advisories for this library
**Testing Implications**
- Does this affect test utilities?
- Are there breaking changes in test APIs?
- Do existing tests still cover the same scenarios?
**Changelog Review**
- Read release notes for breaking changes
- Note any behavioral changes
- Check migration guides if major version
## What to SKIP
**Full Architecture Review** - No code changed, patterns unchanged
**Code Style Review** - No code to review
**New Test Requirements** - Unless API changed significantly
**Security Deep-Dive** - Unless crypto/auth/networking library
**Performance Analysis** - Unless release notes mention performance changes
## Red Flags (Escalate to Full Review)
🚩 **Major version bump** (e.g., 1.x → 2.0) - Read `checklists/feature-addition.md`
🚩 **Security/crypto library** - Read `reference/architectural-patterns.md` and `docs/ARCHITECTURE.md#security`
🚩 **Breaking changes in release notes** - Read relevant code sections carefully
🚩 **Multiple dependency updates at once** - Check for interaction risks
🚩 **Beta/Alpha versions** - Assess stability concerns and rollback plan
If any red flags present, escalate to more comprehensive review using appropriate checklist.
## Prioritizing Findings
Use `reference/priority-framework.md` to classify findings as Critical/Important/Suggested/Optional.
## Output Format
Follow the format guidance from `SKILL.md` Step 5 (concise summary with critical issues only, detailed inline comments with `<details>` tags).
```markdown
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue with file:line reference]
See inline comments for all issue details.
```
## Example Reviews
### Example 1: Simple Patch Version (No Critical Issues)
```markdown
**Overall Assessment:** APPROVE
See inline comments for all issue details.
```
**Inline comment example:**
```
**libs.versions.toml:45** - SUGGESTED: Beta version in production
<details>
<summary>Details</summary>
androidx.credentials updated from 1.5.0 to 1.6.0-beta03
Monitor for stability issues - beta releases may have unexpected behavior in production.
Changelog: Adds support for additional credential types, internal bug fixes.
</details>
```
### Example 2: Major Version with Breaking Changes (With Critical Issues)
```markdown
**Overall Assessment:** REQUEST CHANGES
**Critical Issues:**
- Breaking API changes in Retrofit 3.0.0 (network/api/BitwardenApiService.kt)
- Breaking API changes in Retrofit 3.0.0 (network/api/VaultApiService.kt)
See inline comments for migration details.
```
**Inline comment example:**
```
**network/api/BitwardenApiService.kt:15** - CRITICAL: Breaking API changes
<details>
<summary>Details and fix</summary>
Retrofit 3.0.0 removes `Call<T>` return type. Migration required:
\```kotlin
// Before
fun getUser(): Call<UserResponse>
// After
suspend fun getUser(): Response<UserResponse>
\```
Update all API service interfaces to use suspend functions, update call sites to use coroutines instead of enqueue/execute, and update tests accordingly.
Consider creating a separate PR for this migration due to scope.
Reference: https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-300
</details>
```

View File

@@ -0,0 +1,380 @@
# Feature Addition Review Checklist
## Multi-Pass Strategy
### First Pass: High-Level Assessment
<thinking>
Before diving into details:
1. What is this feature supposed to do?
2. How does it fit into the existing architecture?
3. What are the security implications?
4. What's the scope? (files touched, modules affected)
5. What are the highest-risk areas?
</thinking>
**1. Understand the feature:**
- Read PR description - what problem does this solve?
- Identify user-facing changes vs internal changes
- Note any security implications (auth, encryption, data handling)
**2. Scan file structure:**
- Which modules affected? (app, data, network, ui, core?)
- Are files organized correctly per module structure?
- Any new public APIs introduced?
**3. Initial risk assessment:**
- Does this touch sensitive data or security-critical paths?
- Does this affect existing features or only add new ones?
- Are there obvious compilation or null safety issues?
### Second Pass: Architecture Deep-Dive
<thinking>
Verify architectural integrity:
1. Does this follow MVVM + UDF pattern?
2. Is Hilt DI used correctly?
3. Is state management proper (StateFlow, immutability)?
4. Are modules organized correctly?
5. Is error handling robust (Result types)?
</thinking>
**4. MVVM + UDF Pattern Compliance:**
- ViewModels properly structured?
- State management using StateFlow?
- Business logic in correct layer?
**5. Dependency Injection:**
- Hilt DI used correctly?
- Dependencies injected, not manually instantiated?
- Proper scoping applied?
**6. Module Organization:**
- Code placed in correct modules?
- No circular dependencies introduced?
- Proper separation of concerns?
**7. Error Handling:**
- Using Result types, not exception-based handling?
- Errors propagated correctly through layers?
### Third Pass: Details and Quality
<thinking>
Check quality and completeness:
1. Is code quality high? (null safety, documentation, naming)
2. Are tests comprehensive? (unit + integration)
3. Are there edge cases not covered?
4. Is documentation clear?
5. Are there any code smells or anti-patterns?
</thinking>
**8. Testing:**
- Unit tests for ViewModels and repositories?
- Test coverage for edge cases and error scenarios?
- Tests verify behavior, not implementation?
**9. Code Quality:**
- Null safety handled properly?
- Public APIs have KDoc documentation?
- Naming follows project conventions?
**10. Security:**
- Sensitive data encrypted properly?
- Authentication/authorization handled correctly?
- Zero-knowledge architecture preserved?
## Architecture Review
### MVVM Pattern Compliance
Read `reference/architectural-patterns.md` for detailed patterns.
**ViewModels must:**
- Use `@HiltViewModel` annotation
- Use `@Inject constructor`
- Expose `StateFlow<T>`, NOT `MutableStateFlow<T>` publicly
- Delegate business logic to Repository/Manager
- Avoid direct Android framework dependencies (except ViewModel, SavedStateHandle)
**Common Violations:**
```kotlin
// ❌ BAD - Exposes mutable state
class FeatureViewModel @Inject constructor() : ViewModel() {
val state: MutableStateFlow<State> = MutableStateFlow(State.Initial)
}
// ✅ GOOD - Exposes immutable state
class FeatureViewModel @Inject constructor() : ViewModel() {
private val _state = MutableStateFlow<State>(State.Initial)
val state: StateFlow<State> = _state.asStateFlow()
}
// ❌ BAD - Business logic in ViewModel
fun onSubmit() {
val encrypted = encryptionManager.encrypt(password) // Should be in Repository
_state.value = State.Success
}
// ✅ GOOD - Business logic in Repository, state updated via internal event
fun onSubmit() {
viewModelScope.launch {
// The result of the async operation is captured
val result = repository.submitData(password)
// A single event is sent with the result, not updating state directly
sendAction(FeatureAction.Internal.SubmissionComplete(result))
}
}
// The ViewModel has a handler that processes the internal event
private fun handleInternalAction(action: FeatureAction.Internal) {
when (action) {
is FeatureAction.Internal.SubmissionComplete -> {
// The event handler evaluates the result and updates state
action.result.fold(
onSuccess = { _state.value = State.Success },
onFailure = { _state.value = State.Error(it) }
)
}
}
}
```
**UI Layer must:**
- Only observe state, never modify
- Pass user actions as events to ViewModel
- Contain no business logic
- Use existing UI components from `:ui` module where possible
### Hilt Dependency Injection
Reference: `docs/ARCHITECTURE.md#dependency-injection`
**Required Patterns:**
- ViewModels: `@HiltViewModel` + `@Inject constructor`
- Repositories: `@Inject constructor` on implementation
- Inject interfaces, not concrete implementations
- Modules must provide proper scoping (`@Singleton`, `@ViewModelScoped`)
**Common Violations:**
```kotlin
// ❌ BAD - Manual instantiation
class FeatureViewModel : ViewModel() {
private val repository = FeatureRepositoryImpl()
}
// ✅ GOOD - Injected interface
@HiltViewModel
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepository // Interface, not implementation
) : ViewModel()
// ❌ BAD - Injecting implementation
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepositoryImpl // Should inject interface
)
// ✅ GOOD - Interface injection
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepository // Interface
)
```
### Module Organization
Reference: `docs/ARCHITECTURE.md#module-structure`
**Correct Placement:**
- `:core` - Shared utilities (cryptography, analytics, logging)
- `:data` - Repositories, database, domain models
- `:network` - API clients, network utilities
- `:ui` - Reusable Compose components, theme
- `:app` - Feature screens, ViewModels, navigation
- `:authenticator` - Authenticator app (separate from password manager)
**Check:**
- UI code in `:ui` or `:app` modules
- Data models in `:data`
- Network clients in `:network`
- No circular dependencies between modules
### Error Handling
Reference: `docs/ARCHITECTURE.md#error-handling`
**Required Pattern - Use Result types:**
```kotlin
// ✅ GOOD - Result type
suspend fun fetchData(): Result<Data> = runCatching {
apiService.getData()
}
// ViewModel handles Result
repository.fetchData().fold(
onSuccess = { data -> _state.value = State.Success(data) },
onFailure = { error -> _state.value = State.Error(error) }
)
// ❌ BAD - Exception-based in business logic
suspend fun fetchData(): Data {
try {
return apiService.getData()
} catch (e: Exception) {
throw FeatureException(e) // Don't throw in business logic
}
}
```
## Security Review
Reference: `docs/ARCHITECTURE.md#security`
**Critical Security Checks:**
- **Sensitive data encrypted**: Passwords, keys, tokens use Android Keystore or EncryptedSharedPreferences
- **No plaintext secrets**: No passwords/keys in logs, memory dumps, or SharedPreferences
- **Input validation**: All user-provided data validated and sanitized
- **Authentication tokens**: Securely stored and transmitted
- **Zero-knowledge architecture**: Encryption happens client-side, server never sees plaintext
**Red Flags:**
```kotlin
// ❌ CRITICAL - Plaintext storage
sharedPreferences.edit {
putString("pin", userPin) // Must use EncryptedSharedPreferences
}
// ❌ CRITICAL - Logging sensitive data
Log.d("Auth", "Password: $password") // Never log sensitive data
// ❌ CRITICAL - Weak encryption
val cipher = Cipher.getInstance("DES") // Use AES-256-GCM
// ✅ GOOD - Keystore encryption
val encryptedData = keystoreManager.encrypt(sensitiveData)
secureStorage.store(encryptedData)
```
**If security concerns found, classify as CRITICAL using `reference/priority-framework.md`**
## Testing Review
Reference: `reference/testing-patterns.md`
**Required Test Coverage:**
- **ViewModels**: Unit tests for state transitions, actions, error scenarios
- **Repositories**: Unit tests for data transformations, error handling
- **Business logic**: Unit tests for complex algorithms, calculations
- **Edge cases**: Null inputs, empty states, network failures, concurrent operations
**Test Quality:**
```kotlin
// ✅ GOOD - Tests behavior
@Test
fun `when login succeeds then state updates to success`() = runTest {
val viewModel = LoginViewModel(mockRepository)
coEvery { mockRepository.login(any(), any()) } returns Result.success(User())
viewModel.onLoginClicked("user", "pass")
viewModel.state.test {
assertEquals(LoginState.Success, awaitItem())
}
}
// ❌ BAD - Tests implementation
@Test
fun `repository is called with correct parameters`() {
// This is testing internal implementation, not behavior
}
```
**Testing Frameworks:**
- JUnit 5 for test structure
- MockK for mocking
- Turbine for Flow testing
- Kotlinx-coroutines-test for coroutine testing
## Code Quality
### Null Safety
- No `!!` (non-null assertion) without clear safety guarantee
- Platform types (from Java) handled with explicit nullability
- Nullable types have proper null checks or use safe operators (`?.`, `?:`)
```kotlin
// ❌ BAD - Unsafe assertion
val result = apiService.getData()!! // Could crash
// ✅ GOOD - Safe handling
val result = apiService.getData() ?: return State.Error("No data")
// ❌ BAD - Platform type unchecked
val intent: Intent = getIntent() // Could be null from Java
intent.getStringExtra("key") // Potential NPE
// ✅ GOOD - Explicit nullability
val intent: Intent? = getIntent()
intent?.getStringExtra("key")
```
### Documentation
- **Public APIs**: Have KDoc comments explaining purpose, parameters, return values
- **Complex algorithms**: Explained in comments
- **Non-obvious behavior**: Documented with rationale
```kotlin
// ✅ GOOD - Documented public API
/**
* Encrypts the given data using AES-256-GCM with a key from Android Keystore.
*
* @param plaintext The data to encrypt
* @return Result containing encrypted data or encryption error
*/
suspend fun encrypt(plaintext: ByteArray): Result<EncryptedData>
```
### Style Compliance
Reference: `docs/STYLE_AND_BEST_PRACTICES.md`
Only flag style issues if:
- Not caught by linters (Detekt, ktlint)
- Have architectural implications
- Significantly impact readability
Skip minor formatting (spaces, line breaks, etc.) - linters handle this.
## Prioritizing Findings
Use `reference/priority-framework.md` to classify findings as Critical/Important/Suggested/Optional.
## Providing Feedback
Use `reference/review-psychology.md` for phrasing guidance.
**Key principles:**
- **Ask questions** for design decisions: "Can we use the existing BitwardenTextField component here?"
- **Be prescriptive** for clear violations: "Change MutableStateFlow to StateFlow (MVVM pattern requirement)"
- **Explain rationale**: "This exposes mutable state, violating unidirectional data flow"
- **Use I-statements**: "It's hard for me to understand this logic without comments"
- **Avoid condescension**: Don't use "just", "simply", "obviously"
## Output Format
Follow the format guidance from `SKILL.md` Step 5 (concise summary with critical issues only, detailed inline comments with `<details>` tags).
See `examples/review-outputs.md` for comprehensive feature review example.
```markdown
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue with file:line reference]
See inline comments for all issue details.
```

View File

@@ -0,0 +1,260 @@
# Infrastructure Review Checklist
## Multi-Pass Strategy
### First Pass: Understand the Change
<thinking>
Assess infrastructure change:
1. What problem does this solve?
2. Does this affect production builds, CI/CD, or dev workflow?
3. What's the risk if this breaks?
4. Can this be tested before merge?
5. What's the rollback plan?
</thinking>
**1. Identify the goal:**
- What problem does this solve?
- Is this optimization, fix, or new capability?
- What's the expected impact?
**2. Assess risk:**
- Does this affect production builds?
- Could this break CI/CD pipelines?
- Impact on developer workflow?
**3. Performance implications:**
- Will builds be faster or slower?
- CI time impact?
- Resource usage changes?
### Second Pass: Verify Implementation
<thinking>
Verify configuration and impact:
1. Is the configuration syntax valid?
2. Are secrets/credentials handled securely?
3. What's the impact on build times and CI performance?
4. How will this affect the team's workflow?
5. Is there adequate testing/validation?
</thinking>
**4. Configuration correctness:**
- Syntax valid?
- References correct?
- Secrets/credentials handled securely?
**5. Impact analysis:**
- What workflows/builds are affected?
- Rollback plan if this breaks?
- Documentation for team?
**6. Testing strategy:**
- How can this be tested before merge?
- Canary/gradual rollout possible?
- Monitoring for issues post-merge?
## What to CHECK
**Configuration Correctness**
- YAML/Groovy syntax valid
- File references correct
- Version numbers/tags valid
- Conditional logic sound
**Security**
- No hardcoded secrets or credentials
- GitHub secrets used properly
- Permissions appropriately scoped
- No sensitive data in logs
**Performance Impact**
- Build time implications understood
- CI queue time impact assessed
- Resource usage reasonable
**Rollback Plan**
- Can this be reverted easily?
- Dependencies on other changes?
- Gradual rollout possible?
**Documentation**
- Changes documented for team?
- README or CONTRIBUTING updated?
- Breaking changes clearly noted?
## What to SKIP
**Bikeshedding Configuration** - Unless clear performance/maintenance benefit
**Over-Optimization** - Unless current system has proven problems
**Suggesting Major Rewrites** - Unless current approach is fundamentally broken
## Red Flags
🚩 **Hardcoded secrets** - Use GitHub secrets or secure storage
🚩 **No rollback plan** - Critical infrastructure should be revertible
🚩 **Untested changes** - CI changes should be validated
🚩 **Breaking changes without notice** - Team needs advance warning
🚩 **Performance regression** - Builds shouldn't get significantly slower
## Key Questions to Ask
Use `reference/review-psychology.md` for phrasing:
- "What's the rollback plan if this breaks CI?"
- "Can we test this on a feature branch before main?"
- "Will this impact build times? By how much?"
- "Should this be documented in CONTRIBUTING.md?"
## Common Infrastructure Patterns
### GitHub Actions
```yaml
# ✅ GOOD - Secure, clear, tested
name: Build and Test
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30 # Prevent runaway builds
steps:
- uses: actions/checkout@v4
- name: Run tests
env:
API_KEY: ${{ secrets.API_KEY }} # Secure secret usage
run: ./gradlew test
# ❌ BAD - Insecure, unclear
name: Build
on: push # Too broad, runs on all branches
jobs:
build:
runs-on: ubuntu-latest
# No timeout - could run forever
steps:
- run: |
export API_KEY="hardcoded_key_here" # Hardcoded secret!
./gradlew test
```
### Gradle Configuration
```kotlin
// ✅ GOOD - Clear, maintainable
dependencies {
implementation(libs.androidx.core.ktx) // Version catalog
implementation(libs.hilt.android)
testImplementation(libs.junit5)
testImplementation(libs.mockk)
}
// ❌ BAD - Hardcoded versions
dependencies {
implementation("androidx.core:core-ktx:1.12.0") // Hardcoded version
implementation("com.google.dagger:hilt-android:2.48")
}
```
### Build Optimization
```kotlin
// ✅ GOOD - Parallel, cached
tasks.register("checkAll") {
dependsOn("detekt", "ktlintCheck", "testStandardDebug")
group = "verification"
description = "Run all checks in parallel"
// Enable caching for faster builds
outputs.upToDateWhen { false }
}
// ❌ BAD - Sequential, no caching
tasks.register("checkAll") {
doLast {
exec { commandLine("./gradlew", "detekt") }
exec { commandLine("./gradlew", "ktlintCheck") } // Sequential
exec { commandLine("./gradlew", "test") }
}
}
```
## Prioritizing Findings
Use `reference/priority-framework.md` to classify findings as Critical/Important/Suggested/Optional.
## Output Format
Follow the format guidance from `SKILL.md` Step 5 (concise summary with critical issues only, detailed inline comments with `<details>` tags).
```markdown
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue with file:line reference]
See inline comments for all issue details.
```
## Example Review
```markdown
## Summary
Optimizes CI build by parallelizing test execution and caching dependencies
Impact: Estimated 40% reduction in CI time (12 min → 7 min per build)
## Critical Issues
None
## Suggested Improvements
**.github/workflows/build.yml:23** - Add timeout for safety
```yaml
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30 # Prevent builds from hanging
steps:
# ...
```
This prevents runaway builds if something goes wrong.
**.github/workflows/build.yml:45** - Consider matrix strategy for module tests
Can we run module tests in parallel using a matrix strategy?
```yaml
strategy:
matrix:
module: [app, data, network, ui]
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: ./gradlew :${{ matrix.module }}:test
```
This could further reduce CI time.
**build.gradle.kts:12** - Document caching strategy
Can we add a comment explaining the caching configuration?
Future maintainers will appreciate understanding why these specific cache keys are used.
## Good Practices
- Proper use of GitHub Actions cache
- Parallel test execution
- Version catalog for dependencies
## Action Items
1. Add timeout to build workflow
2. Consider matrix strategy for further parallelization
3. Document caching strategy in build file
## Rollback Plan
If CI breaks:
- Revert commit: `git revert [commit-hash]`
- Previous workflow available at: `.github/workflows/build.yml@main^`
- Monitor CI times at: https://github.com/[org]/[repo]/actions
```

View File

@@ -0,0 +1,234 @@
# Refactoring Review Checklist
## Multi-Pass Strategy
### First Pass: Understand the Refactoring
<thinking>
Analyze the refactoring scope:
1. What pattern is being improved?
2. Why is this refactoring needed?
3. Does this change behavior or just structure?
4. What's the scope? (files affected, migration completeness)
5. What are the risks if something breaks?
</thinking>
**1. Understand the goal:**
- What pattern is being improved?
- Why is this refactoring needed?
- What's the scope of changes?
**2. Assess completeness:**
- Are all instances refactored or just some?
- Are there related areas that should also change?
- Is the migration complete or partial?
**3. Risk assessment:**
- Does this change behavior?
- How many files affected?
- Are tests updated to reflect changes?
### Second Pass: Verify Consistency
<thinking>
Verify refactoring quality:
1. Is the new pattern applied consistently throughout?
2. Are there missed instances of the old pattern?
3. Do tests still pass with same behavior?
4. Is the migration complete or partial?
5. Does this introduce any new issues?
</thinking>
**4. Pattern consistency:**
- Is the new pattern applied consistently throughout?
- Are there missed instances of the old pattern?
- Does this match established project patterns?
**5. Migration completeness:**
- Old pattern fully removed or deprecated?
- All usages updated?
- Documentation updated?
**6. Test coverage:**
- Do tests still pass?
- Are tests refactored to match?
- Does behavior remain unchanged?
## What to CHECK
**Pattern Consistency**
- New pattern applied consistently across all touched code
- Follows established project patterns (MVVM, DI, error handling)
- No mix of old and new patterns
**Migration Completeness**
- All instances of old pattern updated?
- Deprecated methods removed or marked @Deprecated?
- Related code also updated (tests, docs)?
**Behavior Preservation**
- Refactoring doesn't change behavior
- Tests still pass
- Edge cases still handled
**Deprecation Strategy** (if applicable)
- Old APIs marked @Deprecated with migration guidance
- Replacement clearly documented
- Timeline for removal specified
## What to SKIP
**Suggesting Additional Refactorings** - Unless directly related to current changes
**Scope Creep** - Don't request refactoring of untouched code
**Perfection** - Better code is better than perfect code
## Red Flags
🚩 **Incomplete migration** - Mix of old and new patterns
🚩 **Behavior changes** - Refactoring shouldn't change behavior
🚩 **Broken tests** - Tests should be updated to match refactoring
🚩 **Undocumented pattern** - New pattern should be clear to team
## Key Questions to Ask
Use `reference/review-psychology.md` for phrasing:
- "I see the old pattern still used in [file:line] - should that be updated too?"
- "Can we add @Deprecated to the old method with migration guidance?"
- "How do we ensure this behavior remains the same?"
- "Should this pattern be documented in ARCHITECTURE.md?"
## Common Refactoring Patterns
### Extract Interface/Repository
```kotlin
// ✅ GOOD - Complete migration
interface FeatureRepository {
suspend fun getData(): Result<Data>
}
class FeatureRepositoryImpl @Inject constructor(
private val apiService: FeatureApiService
) : FeatureRepository {
override suspend fun getData(): Result<Data> = runCatching {
apiService.fetchData()
}
}
// All usages updated to inject interface
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepository // Interface
) : ViewModel()
// ❌ BAD - Incomplete migration
// Some files still inject FeatureRepositoryImpl directly
```
### Modernize Error Handling
```kotlin
// ✅ GOOD - Complete migration
// Old exception-based removed
suspend fun fetchData(): Result<Data> = runCatching {
apiService.getData()
}
// All call sites updated
repository.fetchData().fold(
onSuccess = { /* handle */ },
onFailure = { /* handle */ }
)
// ❌ BAD - Mixed patterns
// Some functions use Result, others still throw exceptions
```
### Extract Reusable Component
```kotlin
// ✅ GOOD - Complete extraction
// Component moved to :ui module
@Composable
fun BitwardenButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
)
// All usages updated to use new component
// Old inline button implementations removed
// ❌ BAD - Incomplete extraction
// Some screens use new component, others still have inline implementation
```
## Prioritizing Findings
Use `reference/priority-framework.md` to classify findings as Critical/Important/Suggested/Optional.
## Output Format
Follow the format guidance from `SKILL.md` Step 5 (concise summary with critical issues only, detailed inline comments with `<details>` tags).
```markdown
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue with file:line reference]
See inline comments for all issue details.
```
## Example Review
```markdown
## Summary
Refactors authentication flow to use Repository pattern instead of direct Manager access
Scope: 12 files changed, 8 ViewModels updated, Repository interface extracted
## Critical Issues
None - behavior preserved, tests passing
## Suggested Improvements
**app/vault/VaultViewModel.kt:89** - Old pattern still used
This ViewModel still injects AuthManager directly. Should it use AuthRepository like the others?
```kotlin
// Current
class VaultViewModel @Inject constructor(
private val authManager: AuthManager // Old pattern
)
// Should be
class VaultViewModel @Inject constructor(
private val authRepository: AuthRepository // New pattern
)
```
**data/auth/AuthManager.kt:1** - Add deprecation notice
Can we add @Deprecated to AuthManager to guide future development?
```kotlin
@Deprecated(
message = "Use AuthRepository interface instead",
replaceWith = ReplaceWith("AuthRepository"),
level = DeprecationLevel.WARNING
)
class AuthManager
```
**docs/ARCHITECTURE.md** - Document the new pattern
Should we update the architecture docs to reflect this Repository pattern?
The current docs still reference AuthManager as the recommended approach.
## Good Practices
- Repository interface clearly defined
- All data access methods use Result types
- Tests updated to match new pattern
## Action Items
1. Update VaultViewModel to use AuthRepository
2. Add @Deprecated to AuthManager with migration guidance
3. Update ARCHITECTURE.md to document Repository pattern
```

View File

@@ -0,0 +1,243 @@
# UI Refinement Review Checklist
## Multi-Pass Strategy
### First Pass: Visual Changes
<thinking>
Analyze the UI changes:
1. What visual/UX problem is being solved?
2. Are there designs or screenshots to reference?
3. Is this affecting existing screens or new ones?
4. What's the scope of visual changes?
5. Are design tokens (colors, spacing, typography) being used correctly?
</thinking>
**1. Understand the changes:**
- What visual/UX problem is being solved?
- Are there designs or screenshots to reference?
- Is this a bug fix or enhancement?
**2. Component usage:**
- Using existing components from `:ui` module?
- Any new custom components created?
- Could existing components be reused?
### Second Pass: Implementation Review
<thinking>
Check implementation quality:
1. Are Compose best practices followed?
2. Is state hoisting applied correctly?
3. Are existing components reused where possible?
4. Is accessibility properly handled?
5. Does this follow design system patterns?
</thinking>
**3. Compose best practices:**
- Composables properly structured?
- State hoisted correctly?
- Preview composables included?
**4. Accessibility:**
- Content descriptions for images/icons?
- Semantic properties for screen readers?
- Touch targets meet minimum size (48dp)?
**5. Design consistency:**
- Using theme colors, spacing, typography?
- Consistent with other screens?
- Responsive to different screen sizes?
## What to CHECK
**Compose Best Practices**
- Composables are stateless where possible
- State hoisting follows patterns
- Side effects (LaunchedEffect, DisposableEffect) used correctly
- Preview composables provided for development
**Component Reuse**
- Using existing BitwardenButton, BitwardenTextField, etc.?
- Could custom UI be replaced with existing components?
- New reusable components placed in `:ui` module?
**Accessibility**
- `contentDescription` for icons and images
- `semantics` for custom interactions
- Sufficient contrast ratios
- Touch targets ≥ 48dp minimum
**Design Consistency**
- Using `BitwardenTheme` colors (not hardcoded)
- Using `BitwardenTheme` spacing (16.dp, 8.dp, etc.)
- Using `BitwardenTheme` typography styles
- Consistent with existing screen patterns
**Responsive Design**
- Handles different screen sizes?
- Scrollable content where appropriate?
- Landscape orientation considered?
## What to SKIP
**Deep Architecture Review** - Unless ViewModel changes are substantial
**Business Logic Review** - Focus is on presentation, not logic
**Security Review** - Unless UI exposes sensitive data improperly
## Red Flags
🚩 **Duplicating existing components** - Should reuse from `:ui` module
🚩 **Hardcoded colors/dimensions** - Should use theme
🚩 **Missing accessibility properties** - Critical for screen readers
🚩 **State management in UI** - Should be hoisted to ViewModel
## Key Questions to Ask
Use `reference/review-psychology.md` for phrasing:
- "Can we use BitwardenButton here instead of this custom button?"
- "Should this color come from BitwardenTheme instead of being hardcoded?"
- "How will this look on a small screen?"
- "Is there a contentDescription for this icon?"
## Common Patterns
### Composable Structure
```kotlin
// ✅ GOOD - Stateless, hoisted state
@Composable
fun FeatureScreen(
state: FeatureState,
onActionClick: () -> Unit,
modifier: Modifier = Modifier
) {
// UI rendering only
}
// ❌ BAD - Business state in composable
@Composable
fun FeatureScreen() {
var userData by remember { mutableStateOf<User?>(null) } // Business state should be in ViewModel
var isLoading by remember { mutableStateOf(false) } // App state should be in ViewModel
// ...
}
// ✅ OK - UI-local state in composable
@Composable
fun LoginForm(onSubmit: (String, String) -> Unit) {
var username by remember { mutableStateOf("") } // UI-local input state is fine
var password by remember { mutableStateOf("") }
// Hoist only as high as needed
}
```
### Theme Usage
```kotlin
// ✅ GOOD - Using theme
Text(
text = "Title",
style = BitwardenTheme.typography.titleLarge,
color = BitwardenTheme.colorScheme.primary
)
// Design system uses 4.dp increments (4, 8, 12, 16, 24, 32, etc.)
Spacer(modifier = Modifier.height(16.dp))
// ❌ BAD - Hardcoded
Text(
text = "Title",
style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold), // Should use theme
color = Color(0xFF0000FF) // Should use theme color
)
Spacer(modifier = Modifier.height(17.dp)) // Non-standard spacing
```
### Accessibility
```kotlin
// ✅ GOOD - Interactive element with description
Icon(
painter = painterResource(R.drawable.ic_password),
contentDescription = "Password visibility toggle",
modifier = Modifier.clickable { onToggle() }
)
// ✅ GOOD - Decorative icon with explicit null
Icon(
painter = painterResource(R.drawable.ic_check),
contentDescription = null, // Decorative icon next to descriptive text
tint = BitwardenTheme.colorScheme.success
)
// ❌ BAD - Interactive element missing description
Icon(
painter = painterResource(R.drawable.ic_delete),
contentDescription = null, // Interactive elements need descriptions
modifier = Modifier.clickable { onDelete() }
)
```
## Prioritizing Findings
Use `reference/priority-framework.md` to classify findings as Critical/Important/Suggested/Optional.
## Output Format
Follow the format guidance from `SKILL.md` Step 5 (concise summary with critical issues only, detailed inline comments with `<details>` tags).
```markdown
**Overall Assessment:** APPROVE / REQUEST CHANGES
**Critical Issues** (if any):
- [One-line summary of each critical blocking issue with file:line reference]
See inline comments for all issue details.
```
## Example Review
```markdown
## Summary
Updates login screen layout for improved visual hierarchy and touch targets
## Critical Issues
None
## Suggested Improvements
**app/auth/LoginScreen.kt:67** - Can we use BitwardenTextField?
This custom text field looks very similar to `ui/components/BitwardenTextField.kt:89`.
Would using the existing component maintain consistency?
**app/auth/LoginScreen.kt:123** - Add contentDescription
```kotlin
Icon(
painter = painterResource(R.drawable.ic_visibility),
contentDescription = "Show password", // Add for accessibility
modifier = Modifier.clickable { onToggleVisibility() }
)
```
**app/auth/LoginScreen.kt:145** - Use design system spacing
```kotlin
// Current
Spacer(modifier = Modifier.height(17.dp))
// Design system uses 4.dp increments (4, 8, 12, 16, 24, 32, etc.)
Spacer(modifier = Modifier.height(16.dp))
```
## Good Practices
- Proper state hoisting to ViewModel
- Preview composables included
- Responsive layout with ScrollableColumn
## Action Items
1. Evaluate using BitwardenTextField for consistency
2. Add contentDescription for visibility icon
3. Use standard 16.dp spacing
```

View File

@@ -0,0 +1,945 @@
# Review Output Examples
Examples of well-structured code reviews for different change types. Each example demonstrates appropriate depth, tone, and formatting.
---
## CRITICAL: Inline Comments vs Summary Format
### ✅ PREFERRED: Inline Comments on Specific Lines
Create separate inline comment for each specific issue directly on the relevant line:
**Inline Comment 1** (on `app/login/LoginViewModel.kt:78`):
```
**CRITICAL**: Exposes mutable state
Change `MutableStateFlow<State>` to `StateFlow<State>` in public API:
\```kotlin
private val _state = MutableStateFlow<State>()
val state: StateFlow<State> = _state.asStateFlow()
\```
Exposing MutableStateFlow allows external mutation, violating MVVM unidirectional data flow.
Reference: docs/ARCHITECTURE.md#mvvm-pattern
```
**Inline Comment 2** (on `data/auth/BiometricRepository.kt:120`):
```
**CRITICAL**: Missing null safety check
biometricPrompt result can be null. Add explicit null check:
\```kotlin
val result = biometricPrompt.authenticate()
if (result != null) {
processAuth(result)
} else {
handleCancellation()
}
\```
Prevents NPE crash when user cancels biometric prompt.
```
**Summary Comment** (general PR-level comment):
```
## Summary
Implements PIN unlock feature for vault access.
## Overall Assessment
- 2 critical architecture issues found (mutable state, null safety)
- 3 suggestions for improvement (tests, component reuse)
- Good separation of concerns and DI usage
## Recommendation
**REQUEST CHANGES** - Address 2 critical issues before merge.
```
**Why This is Better**:
- Each issue appears directly on the problematic line
- Easy for author to see context and fix
- History preserved when creating new comments
- Other reviewers can see individual discussions
- Cleaner PR conversation thread
---
### ❌ DISCOURAGED: Single Large Summary Comment
Do NOT create one large comment with all issues:
```markdown
## Summary
Implements PIN unlock feature
## Critical Issues
- **app/login/LoginViewModel.kt:78** - Exposes mutable state (entire explanation...)
- **data/auth/BiometricRepository.kt:120** - Missing null safety (entire explanation...)
- **app/vault/UnlockScreen.kt:134** - Should use BitwardenTextField (entire explanation...)
## Suggested Improvements
- **app/vault/UnlockViewModel.kt:92** - Missing test (entire explanation...)
- **app/vault/UnlockViewModel.kt:105** - Consider rate limiting (entire explanation...)
## Good Practices
- Proper Hilt DI
- Clear separation of concerns
## Action Items
1. Fix mutable state exposure
2. Add null safety check
... (20 more items)
```
**Why This is Problematic**:
- All issues in one massive comment
- Author must scroll through entire comment to find each issue
- Hard to track which issues are addressed
- Loses context when jumping between files
- If comment is updated, history is lost
- Other reviewers can't comment on specific issues
---
## Format Guidelines
### Inline Comment Structure
```
**[SEVERITY]**: [One-line issue description]
[Detailed explanation if needed]
[Code example showing problem and/or solution]
[Rationale explaining why this matters]
Reference: [docs link if applicable]
```
**Severity Levels**:
- `**CRITICAL**`: Must fix before merge
- `**IMPORTANT**`: Should fix before merge
- `**SUGGESTED**`: Nice to have
- `**QUESTION**`: Asking for clarification
**Examples**:
```
**CRITICAL**: PIN stored without encryption
Must encrypt using Android Keystore:
\```kotlin
val encrypted = keystoreManager.encrypt(pin.toByteArray())
encryptedPrefs.putBytes(KEY_PIN, encrypted)
\```
Plaintext storage exposes PIN to backups and rooted devices.
Reference: docs/ARCHITECTURE.md#security
```
```
**SUGGESTED**: Consider using BitwardenTextField
Can we use `ui/components/BitwardenTextField.kt:67` instead?
This would maintain consistency across the app.
```
```
**QUESTION**: Is rate limiting needed for PIN attempts?
Currently allows unlimited attempts. Should we add lockout after N failed attempts?
```
### Summary Comment Structure
One summary comment per PR with overall assessment:
```markdown
## Summary
[1-2 sentence description of changes]
## Review Complete
- X critical issues found (see inline comments)
- Y suggestions provided (see inline comments)
- Z good practices noted
## Recommendation
**[APPROVE | REQUEST CHANGES | COMMENT]** - [Reason]
```
---
## Example 1: Dependency Update (Expedited Review)
**Context**: Simple patch version update, no breaking changes
```markdown
## Summary
Updates androidx.credentials from 1.5.0 to 1.6.0-beta03
## Analysis
- **Compilation**: ✓ No breaking changes detected in changelog
- **Security**: ✓ No CVEs addressed
- **Testing**: ✓ No test API changes
- **Changelog**: Adds support for additional credential types, internal bug fixes
## Findings
⚠️ **Beta version** - Monitor for stability issues in production environment
## Recommendation
**APPROVE** - Low-risk minor version bump. Beta status is noted but no blocking concerns identified.
```
**Key Features**:
- Expedited format (no detailed sections)
- Clear analysis checklist
- Single finding noted
- Direct recommendation
---
## Example 2: Dependency Update with Breaking Changes
**Context**: Major version update requiring code migration
```markdown
## Summary
Updates Retrofit from 2.9.0 to 3.0.0
## Analysis
- **Compilation**: ❌ Breaking changes in API
- **Security**: ✓ No security issues
- **Testing**: ⚠️ Test utilities need updates
- **Changelog**: Major rewrite, new Kotlin coroutines API, removed deprecated methods
## Critical Issues
**network/api/BitwardenApiService.kt:34** - API migration required
```kotlin
// Current (deprecated in Retrofit 3.0)
@GET("api/accounts/profile")
fun getProfile(): Call<ProfileResponse>
// Must migrate to
@GET("api/accounts/profile")
suspend fun getProfile(): Response<ProfileResponse>
```
Retrofit 3.0 removes the `Call<T>` return type. All 12 API methods in this file need migration.
**network/api/VaultApiService.kt:45** - Same migration needed
All 8 API methods need to migrate from `Call<T>` to `suspend fun` returning `Response<T>`.
## Suggested Improvements
**Consider separate PR for migration** - Given the scope (20 API methods across 2 files + all call sites + tests), this might be better as a dedicated migration PR with comprehensive testing.
## Recommendation
**REQUEST CHANGES** - Major version requires code migration:
1. Update all API service interfaces to use suspend functions
2. Update all call sites to use coroutines instead of enqueue/execute
3. Update tests to handle new suspend function APIs
4. Consider creating separate PR for this migration due to scope
## Migration Resources
- Retrofit 3.0 migration guide: https://square.github.io/retrofit/changelogs/changelog-3.x/
- Example migration: https://github.com/square/retrofit/tree/master/samples
```
**Key Features**:
- Specific file:line references for each issue
- Code examples showing current vs required
- Scope assessment
- Migration guidance
---
## Example 3: Bug Fix (Focused Review)
**Context**: Fixes crash when biometric prompt is cancelled
```markdown
## Summary
Fixes crash when biometric prompt is cancelled (PM-12345)
Root cause: BiometricPrompt result was nullable but code assumed non-null
## Critical Issues
None
## Suggested Improvements
**data/auth/BiometricRepository.kt:120** - Add regression test
Can we add a test that verifies this specific bug doesn't reoccur?
```kotlin
@Test
fun `when biometric cancelled then returns cancelled state`() = runTest {
coEvery { biometricPrompt.authenticate() } returns null
val result = repository.authenticate()
assertEquals(AuthResult.Cancelled, result)
}
```
This prevents regression of the crash you just fixed.
**data/auth/BiometricRepository.kt:134** - Consider extracting null handling
```kotlin
private fun handleBiometricResult(result: BiometricPrompt.AuthenticationResult?): AuthResult {
return result?.let { AuthResult.Success(it) } ?: AuthResult.Cancelled
}
```
This pattern could be reused if we add other biometric auth points in the future.
**app/auth/BiometricViewModel.kt:89** - Check similar pattern in FaceIdRepository
I see similar biometric handling in `data/auth/FaceIdRepository.kt:78`. Does it have the same issue?
## Good Practices
- Added null safety check for prompt result
- Proper error state propagation through sealed class
- Clear commit message explaining root cause
## Action Items
1. Add regression test for cancellation scenario
2. Check FaceIdRepository for similar null safety issue
3. Consider extracting null handling pattern if reusable
```
**Key Features**:
- Root cause explained
- Specific line references for each suggestion
- Code examples provided
- Questions about related code
- Concise good practices (no elaboration)
---
## Example 4: Feature Addition (Comprehensive Review)
**Context**: Implements PIN unlock for vault access
```markdown
## Summary
Implements PIN unlock feature for vault access (PM-54321)
Adds new authentication option allowing users to unlock vault with 4-6 digit PIN instead of master password. Includes PIN creation, validation, and secure storage.
## Critical Issues
**app/vault/unlock/UnlockViewModel.kt:78** - Exposes mutable state
```kotlin
// Current (problematic)
val unlockState: MutableStateFlow<UnlockState>
// Should be
private val _unlockState = MutableStateFlow<UnlockState>()
val unlockState: StateFlow<UnlockState> = _unlockState.asStateFlow()
```
Exposing MutableStateFlow allows external code to modify ViewModel state directly, violating MVVM's unidirectional data flow principle.
Reference: `docs/ARCHITECTURE.md#mvvm-pattern`
**data/vault/UnlockRepository.kt:145** - SECURITY: PIN stored without encryption
```kotlin
// Current (CRITICAL SECURITY ISSUE)
sharedPreferences.edit {
putString(KEY_PIN, pin)
}
// Must use Android Keystore encryption
suspend fun storePin(pin: String): Result<Unit> = runCatching {
val encrypted = keystoreManager.encrypt(pin.toByteArray())
encryptedPrefs.putBytes(KEY_PIN, encrypted)
}
```
Storing PIN in plaintext SharedPreferences exposes it to backup systems and rooted devices.
Must use Android Keystore encryption or EncryptedSharedPreferences.
Reference: `docs/ARCHITECTURE.md#security`
## Suggested Improvements
**app/vault/unlock/UnlockViewModel.kt:92** - Missing error handling test
```kotlin
@Test
fun `when incorrect PIN entered then returns error state`() = runTest {
val viewModel = UnlockViewModel(mockRepository)
coEvery { mockRepository.validatePin("1234") } returns Result.failure(InvalidPinException())
viewModel.onPinEntered("1234")
assertEquals(UnlockState.Error("Invalid PIN"), viewModel.state.value)
}
```
Add test to prevent regression if error handling changes.
**app/vault/unlock/UnlockViewModel.kt:105** - Consider rate limiting
Can we add rate limiting for PIN attempts? Currently allows unlimited attempts, which could enable brute force attacks.
```kotlin
private var attemptCount = 0
private var lockoutUntil: Instant? = null
fun onPinEntered(pin: String) {
if (isLockedOut()) {
_state.value = UnlockState.LockedOut(lockoutUntil!!)
return
}
// ... validate PIN ...
if (invalid) {
attemptCount++
if (attemptCount >= MAX_ATTEMPTS) {
lockoutUntil = clock.millis() + 15.minutes
}
}
}
```
**app/vault/unlock/UnlockScreen.kt:134** - Can we use BitwardenTextField?
This custom PIN input field looks similar to `ui/components/BitwardenTextField.kt:67`.
Would using the existing component maintain consistency?
**data/vault/UnlockRepository.kt:178** - Add test for PIN length validation
```kotlin
@Test
fun `when PIN is less than 4 digits then returns validation error`() = runTest {
val result = repository.createPin("123")
assertTrue(result.isFailure)
assertIs<ValidationException>(result.exceptionOrNull())
}
```
## Good Practices
- Proper Hilt DI usage throughout
- Clear separation of UI/ViewModel/Repository layers
- Sealed classes for state management
- Comprehensive happy-path unit tests
## Action Items
1. **MUST FIX**: Encrypt PIN using Android Keystore (security issue)
2. **MUST FIX**: Expose immutable StateFlow in ViewModel (architecture violation)
3. **SHOULD ADD**: Test for incorrect PIN error flow
4. **SHOULD ADD**: Test for PIN length validation
5. **CONSIDER**: Rate limiting for PIN attempts (security enhancement)
6. **CONSIDER**: Evaluate BitwardenTextField for consistency
```
**Key Features**:
- Comprehensive review with multiple sections
- Critical security issues clearly flagged
- Specific code examples for fixes
- Mix of prescriptive fixes and collaborative questions
- Test examples provided
- Clear prioritization (MUST FIX vs SHOULD ADD vs CONSIDER)
---
## Example 5: UI Refinement (Design-Focused Review)
**Context**: Updates login screen layout for improved visual hierarchy
```markdown
## Summary
Updates login screen layout for improved visual hierarchy and touch target sizes
Adjusts spacing, increases button sizes to meet accessibility guidelines, and improves visual flow.
## Critical Issues
None
## Suggested Improvements
**app/auth/LoginScreen.kt:67** - Can we use BitwardenTextField?
This custom text field implementation looks very similar to `ui/components/BitwardenTextField.kt:89`.
```kotlin
// Current (custom implementation)
OutlinedTextField(
value = email,
onValueChange = onEmailChange,
colors = OutlinedTextFieldDefaults.colors(...)
)
// Consider using existing component
BitwardenTextField(
value = email,
onValueChange = onEmailChange,
label = "Email"
)
```
Using the existing component would maintain consistency across the app and reduce code duplication.
**app/auth/LoginScreen.kt:123** - Add contentDescription for accessibility
```kotlin
Icon(
painter = painterResource(R.drawable.ic_visibility),
contentDescription = "Show password", // Add this
modifier = Modifier.clickable { onToggleVisibility() }
)
```
Screen readers need contentDescription to announce the icon's purpose to visually impaired users.
**app/auth/LoginScreen.kt:145** - Use theme spacing
```kotlin
// Current (non-standard spacing)
Spacer(modifier = Modifier.height(17.dp))
// Should use standard theme spacing
Spacer(modifier = Modifier.height(16.dp))
```
Project uses standard spacing values (4dp, 8dp, 16dp, 24dp) for consistency.
**app/auth/LoginScreen.kt:178** - Use theme color
```kotlin
// Current (hardcoded color)
color = Color(0xFF0066FF)
// Should use theme
color = BitwardenTheme.colorScheme.primary
```
## Good Practices
- Proper state hoisting to ViewModel
- Preview composables included for development
- Touch targets meet 48dp minimum
## Action Items
1. Evaluate using BitwardenTextField for consistency
2. Add contentDescription for visibility icon
3. Use standard 16dp spacing instead of 17dp
4. Use theme color instead of hardcoded value
```
**Key Features**:
- Design and accessibility focused
- Specific line references
- Shows current vs recommended code
- Explains rationale (consistency, accessibility)
- Notes good practices briefly
---
## Example 6: Refactoring (Pattern Consistency Review)
**Context**: Refactors authentication to use Repository pattern
```markdown
## Summary
Refactors authentication flow to use Repository pattern instead of direct Manager access
Scope: 12 files changed, 8 ViewModels updated, AuthRepository interface extracted
## Critical Issues
None - behavior preserved, all tests passing
## Suggested Improvements
**app/vault/VaultViewModel.kt:89** - Old pattern still used here
This ViewModel still injects AuthManager directly. Should it use AuthRepository like the others?
```kotlin
// Current (old pattern)
class VaultViewModel @Inject constructor(
private val authManager: AuthManager
)
// Should be (new pattern)
class VaultViewModel @Inject constructor(
private val authRepository: AuthRepository
)
```
This is the only ViewModel still using the old pattern.
**data/auth/AuthManager.kt:1** - Add deprecation notice
Can we add @Deprecated to AuthManager to guide future development?
```kotlin
@Deprecated(
message = "Use AuthRepository interface instead. AuthManager will be removed in v3.0.",
replaceWith = ReplaceWith("AuthRepository"),
level = DeprecationLevel.WARNING
)
class AuthManager @Inject constructor(...)
```
This helps other developers understand the migration path and prevents new code from using the old pattern.
**docs/ARCHITECTURE.md:145** - Document the new pattern
Should we update the architecture docs to reflect this Repository pattern?
The current documentation (line 145-167) still shows AuthManager as the recommended approach.
Suggest adding section:
```markdown
## Authentication Architecture
Use `AuthRepository` interface for all authentication operations:
- Login/logout
- Session management
- Token refresh
The repository pattern provides abstraction and makes testing easier.
```
## Good Practices
- Repository interface clearly defined with Result return types
- All ViewModels except one successfully migrated
- Tests updated to match new pattern
- Behavior preserved (all existing tests pass)
## Action Items
1. Update VaultViewModel to use AuthRepository
2. Add @Deprecated annotation to AuthManager with timeline
3. Update ARCHITECTURE.md to document Repository pattern
4. Consider deprecation timeline (suggest v3.0 removal)
```
**Key Features**:
- Focuses on migration completeness
- Identifies incomplete migration (one missed file)
- Suggests deprecation strategy
- Notes documentation needs
- Acknowledges good practices briefly
---
## Key Patterns Across All Examples
### Consistent Elements
1. **File:Line References**: Every specific issue includes `file:line_number` format
2. **Code Examples**: Complex issues show current code and suggested fix
3. **Rationale**: Explains **why** change is needed, not just **what**
4. **Prioritization**: Clear distinction between Critical, Suggested, Optional
5. **Actionable**: Specific steps the author should take
6. **Constructive Tone**: Questions for design decisions, prescriptive for violations
### Format Structure
```markdown
## Summary
[1-2 sentence description]
## Critical Issues (if any)
**file:line** - Issue description
[Code example showing problem and solution]
[Rationale explaining why this matters]
## Suggested Improvements
**file:line** - Suggestion with question or explanation
[Code example if helpful]
[Benefits of the suggestion]
## Good Practices (brief)
- Item 1
- Item 2
- Item 3
## Action Items
1. Required action
2. Recommended action
3. Optional consideration
```
---
## Anti-Patterns to Avoid
### ❌ Too Vague
```
The state management could be better.
This code has issues.
Consider improving the architecture.
```
### ❌ Too Verbose
```
## Good Practices
- Excellent Hilt DI usage! I really appreciate how you've implemented dependency injection here. The use of @HiltViewModel is exactly what we want to see, and injecting interfaces instead of implementations is a best practice that really shines in this PR. The constructor injection pattern is clean and testable. Great work! This is a perfect example of how to structure ViewModels in our codebase. Keep up the fantastic work! 👍🎉
```
### ❌ No Specifics
```
There are some null safety issues in the ViewModel.
Tests are missing for some scenarios.
```
### ✅ Good Specificity
```
**app/login/LoginViewModel.kt:78** - Missing null safety check
biometricPrompt result can be null. Add explicit check to prevent NPE.
```
---
## Concise Review Format (Recommended)
### Key Principles
1. **Minimal summary**: Only verdict + critical issues
2. **Collapsible inline comments**: Severity + one-line description visible, details collapsed
3. **No duplication**: Don't repeat inline issues in summary
4. **No redundant sections**: No "Action Items" or "Good Practices" in summary
### Example 1: Feature Review with Multiple Issues
**Summary Comment:**
```markdown
**Overall Assessment:** REQUEST CHANGES
**Critical Issues:**
- Exposes mutable state (app/vault/VaultViewModel.kt:45)
- Missing null safety check (app/vault/VaultRepository.kt:123)
See inline comments for all issue details.
```
**Inline Comments:**
```markdown
**app/vault/VaultViewModel.kt:45** - CRITICAL: Exposes mutable state
<details>
<summary>Details and fix</summary>
Change to private backing field pattern:
\```kotlin
private val _vaultState = MutableStateFlow<VaultState>(VaultState.Loading)
val vaultState: StateFlow<VaultState> = _vaultState.asStateFlow()
\```
Exposing MutableStateFlow allows external mutation, violating MVVM unidirectional data flow.
Reference: docs/ARCHITECTURE.md#mvvm-pattern
</details>
```
```markdown
**app/vault/VaultRepository.kt:123** - CRITICAL: Missing null safety check
<details>
<summary>Details and fix</summary>
Add null check before accessing cipher:
\```kotlin
val cipher = getCipher(id) ?: return Result.failure(CipherNotFoundException())
\```
Without null safety, this will crash when cipher ID is invalid.
</details>
```
```markdown
**app/vault/VaultViewModel.kt:89** - IMPORTANT: Missing test coverage
<details>
<summary>Details</summary>
Add test for error state handling:
\```kotlin
@Test
fun `when load fails then shows error state`() = runTest {
coEvery { repository.getVaultItems() } returns Result.failure(Exception())
viewModel.loadVault()
assertEquals(VaultState.Error, viewModel.vaultState.value)
}
\```
Error paths should be tested to prevent regressions.
Reference: reference/testing-patterns.md
</details>
```
**Why this works:**
- Summary is 4 lines (vs 30+ lines with verbose format)
- Severity + issue visible immediately
- Full details available on expansion
- Zero duplication between summary and inline comments
- Token-efficient while preserving all information
---
### Example 2: Dependency Update (No Critical Issues)
**Summary Comment:**
```markdown
**Overall Assessment:** APPROVE
See inline comments for suggested improvements.
```
**Inline Comment:**
```markdown
**gradle/libs.versions.toml:45** - SUGGESTED: Beta version in production
<details>
<summary>Details</summary>
Updated androidx.credentials from 1.5.0 to 1.6.0-beta03.
Monitor for stability issues - beta releases may have unexpected behavior in production.
Changelog: Adds support for additional credential types, internal bug fixes.
</details>
```
**Why this works:**
- Immediate approval visible (no critical issues)
- Suggestion collapsed to reduce noise
- All context preserved for interested reviewers
---
### Example 3: Bug Fix Review
**Summary Comment:**
```markdown
**Overall Assessment:** APPROVE
See inline comments for suggested improvements.
```
**Inline Comments:**
```markdown
**data/auth/BiometricRepository.kt:120** - SUGGESTED: Extract null handling
<details>
<summary>Details</summary>
Root cause: BiometricPrompt result was nullable but code assumed non-null, causing crash on cancellation (PM-12345).
Consider extracting pattern for reuse:
\```kotlin
private fun handleBiometricResult(result: BiometricPrompt.AuthenticationResult?): AuthResult {
return result?.let { AuthResult.Success(it) } ?: AuthResult.Cancelled
}
\```
</details>
```
```markdown
**app/auth/BiometricViewModel.kt:89** - SUGGESTED: Add regression test
<details>
<summary>Details</summary>
\```kotlin
@Test
fun `when biometric cancelled then returns cancelled state`() = runTest {
coEvery { repository.authenticate() } returns Result.failure(CancelledException())
viewModel.onBiometricAuth()
assertEquals(AuthState.Cancelled, viewModel.state.value)
}
\```
Prevents regression of the bug just fixed.
</details>
```
**Why this works:**
- Approval decision immediately visible
- Root cause analysis preserved but collapsed
- Suggestions don't overwhelm the fix
- Test recommendations available but not blocking
---
### Comparison: Verbose vs Concise
**Verbose Format (Old):**
```markdown
## Summary
Adds vault item encryption feature
Root cause: Feature implements client-side encryption for vault items
## Critical Issues
**app/vault/VaultViewModel.kt:45** - Exposes mutable state
Change MutableStateFlow to StateFlow:
\```kotlin
private val _state = MutableStateFlow()
val state = _state.asStateFlow()
\```
Prevents external mutation, enforces unidirectional data flow.
Reference: docs/ARCHITECTURE.md
**app/vault/VaultRepository.kt:123** - Missing null safety
Add null check: cipher ?: return Result.failure()
## Important Issues
[3 more issues with full details]
## Suggested Improvements
[5 more issues with full details]
## Good Practices
- Clean MVVM separation
- Proper Hilt DI usage
- Comprehensive test coverage
## Action Items
1. Fix mutable state exposure (app/vault/VaultViewModel.kt:45)
2. Add null safety (app/vault/VaultRepository.kt:123)
3. [8 more action items duplicating the issues above]
```
**Token count:** ~800-1000 tokens
**Issues:** Heavy duplication, verbose praise, action items redundant with inline comments
**Concise Format (New):**
```markdown
**Overall Assessment:** REQUEST CHANGES
**Critical Issues:**
- Exposes mutable state (app/vault/VaultViewModel.kt:45)
- Missing null safety (app/vault/VaultRepository.kt:123)
See inline comments for all issue details.
```
Plus inline comments with `<details>` tags.
**Token count:** ~200-300 tokens visible, ~600-800 total (expandable)
**Benefits:**
- 60-70% token reduction
- Zero duplication
- Faster scanning
- All details preserved
---
### Implementation Notes
**When to use which format:**
**Use Concise Format for:**
- All reviews going forward (new default)
- High token efficiency needed
- Multiple issues to report
- When details would overwhelm
**Visible Content (Not Collapsed):**
- Severity level
- One-line issue description
- File:line reference
**Collapsed Content (In `<details>`):**
- Code examples (before/after)
- Detailed rationale
- References to documentation
- Implementation suggestions
**Never Include in Summary:**
- Issue details (those are in inline comments)
- Good Practices section (eliminates noise)
- Action Items (duplicates inline comments)

View File

@@ -0,0 +1,296 @@
# Architectural Patterns Quick Reference
Quick reference for Bitwarden Android architectural patterns during code reviews. For comprehensive details, read `docs/ARCHITECTURE.md` and `docs/STYLE_AND_BEST_PRACTICES.md`.
## MVVM + UDF Pattern
### ViewModel Structure
**✅ GOOD - Proper state encapsulation**:
```kotlin
@HiltViewModel
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepository
) : ViewModel() {
// Private mutable state
private val _state = MutableStateFlow<FeatureState>(FeatureState.Initial)
// Public immutable state
val state: StateFlow<FeatureState> = _state.asStateFlow()
// Actions as functions, state updated via internal action
fun onActionClicked() {
viewModelScope.launch {
val result = repository.performAction()
sendAction(FeatureAction.Internal.ActionComplete(result))
}
}
// The ViewModel has a handler that processes the internal action
private fun handleInternalAction(action: FeatureAction.Internal) {
when (action) {
is FeatureAction.Internal.ActionComplete -> {
// The action handler evaluates the result and updates state
action.result.fold(
onSuccess = { _state.value = State.Success },
onFailure = { _state.value = State.Error(it) }
)
}
}
}
}
```
**❌ BAD - Common violations**:
```kotlin
class FeatureViewModel : ViewModel() {
// ❌ Exposes mutable state
val state: MutableStateFlow<FeatureState>
// ❌ Business logic in ViewModel
fun onSubmit() {
val encrypted = encryptionManager.encrypt(data) // Should be in Repository
_state.value = FeatureState.Success
}
// ❌ Direct Android framework dependency
fun onCreate(context: Context) { // ViewModels shouldn't depend on Context
// ...
}
}
```
**Key Rules**:
- Expose `StateFlow<T>`, never `MutableStateFlow<T>`
- Delegate business logic to Repository/Manager
- No direct Android framework dependencies (except ViewModel, SavedStateHandle)
- Use `viewModelScope` for coroutines
Reference: `docs/ARCHITECTURE.md#mvvm-pattern`
---
### UI Layer (Compose)
**✅ GOOD - Stateless, observes only**:
```kotlin
@Composable
fun FeatureScreen(
state: FeatureState,
onActionClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
when (state) {
is FeatureState.Loading -> LoadingIndicator()
is FeatureState.Success -> SuccessContent(state.data)
is FeatureState.Error -> ErrorMessage(state.error)
}
BitwardenButton(
text = "Action",
onClick = onActionClick // Sends event to ViewModel
)
}
}
```
**❌ BAD - Stateful, modifies state**:
```kotlin
@Composable
fun FeatureScreen(viewModel: FeatureViewModel) {
var localState by remember { mutableStateOf(...) } // ❌ State in UI
Button(onClick = {
viewModel._state.value = FeatureState.Loading // ❌ Directly modifying ViewModel state
})
}
```
**Key Rules**:
- Compose screens observe state, never modify
- User actions passed as events/callbacks to ViewModel
- No business logic in UI layer
- Use existing components from `:ui` module
---
## Hilt Dependency Injection
### ViewModels
**✅ GOOD - Interface injection**:
```kotlin
@HiltViewModel
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepository, // Interface, not implementation
private val authManager: AuthManager,
savedStateHandle: SavedStateHandle
) : ViewModel()
```
**❌ BAD - Common violations**:
```kotlin
// ❌ No @HiltViewModel annotation
class FeatureViewModel @Inject constructor(...)
// ❌ Injecting implementation instead of interface
class FeatureViewModel @Inject constructor(
private val repository: FeatureRepositoryImpl // Should inject interface
)
// ❌ Manual instantiation
class FeatureViewModel : ViewModel() {
private val repository = FeatureRepositoryImpl() // Should use @Inject
}
```
**Key Rules**:
- Annotate with `@HiltViewModel`
- Use `@Inject constructor`
- Inject interfaces, not implementations
- Use `SavedStateHandle` for process death survival
Reference: `docs/ARCHITECTURE.md#dependency-injection`
---
### Repositories and Managers
**✅ GOOD - Implementation with @Inject**:
```kotlin
interface FeatureRepository {
suspend fun fetchData(): Result<Data>
}
class FeatureRepositoryImpl @Inject constructor(
private val apiService: FeatureApiService,
private val database: FeatureDao
) : FeatureRepository {
override suspend fun fetchData(): Result<Data> = runCatching {
apiService.getData()
}
}
```
**Module provides interface**:
```kotlin
@Module
@InstallIn(SingletonComponent::class)
abstract class DataModule {
@Binds
@Singleton
abstract fun bindFeatureRepository(
impl: FeatureRepositoryImpl
): FeatureRepository
}
```
**Key Rules**:
- Define interface for abstraction
- Implementation uses `@Inject constructor`
- Module binds implementation to interface
- Appropriate scoping (`@Singleton`, `@ViewModelScoped`)
---
## Module Organization
```
android/
├── core/ # Shared utilities (cryptography, analytics, logging)
├── data/ # Repositories, database, domain models
├── network/ # API clients, network utilities
├── ui/ # Reusable Compose components, theme
├── app/ # Application, feature screens, ViewModels
└── authenticator/ # Authenticator app (separate from password manager)
```
**Correct Placement**:
- UI screens and ViewModels → `:app`
- Reusable Compose components → `:ui`
- Data models and Repositories → `:data`
- API services → `:network`
- Cryptography, logging → `:core`
**Check for**:
- No circular dependencies
- Correct module placement
- Proper visibility (internal vs public)
Reference: `docs/ARCHITECTURE.md#module-structure`
---
## Error Handling
### Use Result Types, Not Exceptions
**✅ GOOD - Result-based**:
```kotlin
// Repository
suspend fun fetchData(): Result<Data> = runCatching {
apiService.getData()
}
// ViewModel
fun onFetch() {
viewModelScope.launch {
val result = repository.fetchData()
sendAction(FeatureAction.Internal.FetchComplete(result))
}
}
```
**❌ BAD - Exception-based in business logic**:
```kotlin
// ❌ Don't throw in business logic
suspend fun fetchData(): Data {
try {
return apiService.getData()
} catch (e: Exception) {
throw FeatureException(e) // Don't throw in repositories
}
}
// ❌ Try-catch in ViewModel
fun onFetch() {
viewModelScope.launch {
try {
val data = repository.fetchData()
sendAction(FeatureAction.Internal.FetchComplete(data))
} catch (e: Exception) {
sendAction(FeatureAction.Internal.FetchComplete(e))
}
}
}
```
**Key Rules**:
- Use `Result<T>` return types in repositories
- Use `runCatching { }` to wrap API calls
- Handle results with `.fold()` in ViewModels
- Don't throw exceptions in business logic
Reference: `docs/ARCHITECTURE.md#error-handling`
---
## Quick Checklist
### Architecture
- [ ] ViewModels expose StateFlow, not MutableStateFlow?
- [ ] Business logic in Repository, not ViewModel?
- [ ] Using Hilt DI (@HiltViewModel, @Inject constructor)?
- [ ] Injecting interfaces, not implementations?
- [ ] Correct module placement?
### Error Handling
- [ ] Using Result types, not exceptions in business logic?
- [ ] Errors handled with .fold() in ViewModels?
---
For comprehensive details, always refer to:
- `docs/ARCHITECTURE.md` - Full architecture patterns
- `docs/STYLE_AND_BEST_PRACTICES.md` - Complete style guide

View File

@@ -0,0 +1,274 @@
# Issue Priority Framework
Use this framework to classify findings during code review. Clear prioritization helps authors triage and address issues effectively.
## Critical (Blocker - Must Fix Before Merge)
These issues **must** be addressed before the PR can be merged. They pose immediate risks to security, stability, or architecture integrity.
### Security
- Data leaks or plaintext sensitive data (passwords, keys, tokens)
- Weak encryption or insecure key storage
- Missing authentication or authorization checks
- Input injection vulnerabilities (SQL, XSS, command injection)
- Sensitive data in logs or error messages
**Example**:
```
**data/vault/VaultRepository.kt:145** - CRITICAL: PIN stored without encryption
PIN must be encrypted using Android Keystore, not stored in plaintext SharedPreferences.
Reference: docs/ARCHITECTURE.md#security
```
### Stability
- Compilation errors or warnings
- Null pointer exceptions in production paths
- Resource leaks (file handles, network connections, memory)
- Crashes or unhandled exceptions in critical paths
- Thread safety violations
**Example**:
```
**app/auth/BiometricRepository.kt:120** - CRITICAL: Missing null safety check
biometricPrompt result can be null. Add explicit null check to prevent crash.
```
### Architecture
- Mutable state exposure in ViewModels (violates MVVM)
- Exception-based error handling in business logic (should use Result)
- Circular dependencies between modules
- Violation of zero-knowledge principles
- Direct dependency instantiation (should use DI)
**Example**:
```
**app/login/LoginViewModel.kt:45** - CRITICAL: Exposes mutable state
Change MutableStateFlow to StateFlow in public API to prevent external state mutation.
This violates MVVM encapsulation pattern.
```
---
## Important (Should Fix)
These issues should be addressed but don't block merge if there's a compelling reason. They improve code quality, maintainability, or robustness.
### Testing
- Missing tests for critical paths (authentication, encryption, data sync)
- Missing tests for new public APIs
- Tests that don't verify actual behavior (test implementation, not behavior)
- Missing test coverage for error scenarios
**Example**:
```
**data/auth/BiometricRepository.kt** - IMPORTANT: Missing test for cancellation
Add test for user cancellation scenario to prevent regression.
```
### Architecture
- Inconsistent patterns within PR (mixing error handling approaches)
- Poor separation of concerns
- Tight coupling between components
- Not following established project patterns
**Example**:
```
**app/vault/VaultViewModel.kt:89** - IMPORTANT: Business logic in ViewModel
Encryption logic should be in Repository, not ViewModel.
Reference: docs/ARCHITECTURE.md#mvvm-pattern
```
### Documentation
- Undocumented public APIs (missing KDoc)
- Missing documentation for complex algorithms
- Unclear naming or confusing interfaces
**Example**:
```
**core/crypto/EncryptionManager.kt:34** - IMPORTANT: Missing KDoc
Public encryption method should document parameters, return value, and exceptions.
```
### Performance
- Inefficient algorithms in hot paths (with evidence from profiling)
- Blocking main thread with I/O operations
- Memory inefficient data structures (with evidence)
**Example**:
```
**app/vault/VaultListViewModel.kt:78** - IMPORTANT: N+1 query pattern
Fetching items one-by-one in loop. Consider batch fetch to reduce database queries.
```
---
## Suggested (Nice to Have)
These are improvement opportunities but not required. Consider the effort vs. benefit before requesting changes.
### Code Quality
- Minor style inconsistencies (if not caught by linter)
- Opportunities for DRY improvements
- Better variable naming for clarity
- Simplification opportunities
**Example**:
```
**app/vault/VaultScreen.kt:145** - SUGGESTED: Consider extracting helper function
This 20-line block appears in 3 places. Consider extracting to reduce duplication.
```
### Testing
- Additional test coverage for edge cases (beyond critical paths)
- More comprehensive integration tests
- Performance tests for non-critical paths
**Example**:
```
**app/login/LoginViewModelTest.kt** - SUGGESTED: Add test for concurrent login attempts
Not critical, but would increase confidence in edge case handling.
```
### Refactoring
- Extracting reusable patterns
- Modernizing old patterns (if touching related code)
- Improving testability
**Example**:
```
**data/vault/VaultRepository.kt:200** - SUGGESTED: Consider extracting validation logic
Could be extracted to separate validator class for reusability and testing.
```
---
## Optional (Acknowledge But Don't Require)
Note good practices to reinforce positive patterns. Keep these **brief** - list only, no elaboration.
### Good Practices
**Format**: Simple bullet list, no explanation
```markdown
## Good Practices
- Proper Hilt DI usage throughout
- Comprehensive unit test coverage
- Clear separation of concerns
- Well-documented public APIs
```
**Don't do this** (too verbose):
```markdown
## Good Practices
- Proper Hilt DI usage throughout: Great job using @Inject constructor and injecting interfaces! This follows our established patterns perfectly and makes the code very testable. Really excellent work here! 👍
```
---
## Classification Guidelines
### When Something is Between Categories
**If unsure between Critical and Important**:
- Ask: "Could this cause production incidents, data loss, or security breaches?"
- If yes → Critical
- If no → Important
**If unsure between Important and Suggested**:
- Ask: "Would I block merge over this?"
- If yes → Important
- If no → Suggested
**If unsure between Suggested and Optional**:
- Ask: "Is this actionable feedback or just acknowledgment?"
- If actionable → Suggested
- If acknowledgment → Optional
### Context Matters
**Same issue, different contexts**:
```
// Critical for production code
Missing null safety check in auth flow → CRITICAL
// Suggested for internal test utility
Missing null safety check in test helper → SUGGESTED
```
**Same pattern, different risk levels**:
```
// Critical for new feature
Missing tests for new auth method → CRITICAL
// Important for bug fix
Missing regression test → IMPORTANT
// Suggested for refactoring
Missing tests for refactored helper → SUGGESTED
```
---
## Examples by Change Type
### Dependency Update
- **Critical**: Known CVEs in old version not addressed
- **Important**: Breaking changes that need migration
- **Suggested**: Beta/alpha version stability concerns
### Bug Fix
- **Critical**: Fix doesn't address root cause
- **Important**: Missing regression test
- **Suggested**: Similar bugs in related code
### Feature Addition
- **Critical**: Security vulnerabilities, architecture violations
- **Important**: Missing tests for critical paths
- **Suggested**: Additional test coverage, minor refactoring
### UI Refinement
- **Critical**: Missing accessibility for key actions
- **Important**: Not using theme (hardcoded colors)
- **Suggested**: Minor spacing/alignment improvements
### Refactoring
- **Critical**: Changes behavior (should be behavior-preserving)
- **Important**: Incomplete migration (mix of old/new patterns)
- **Suggested**: Additional instances that could be refactored
### Infrastructure
- **Critical**: Hardcoded secrets, no rollback plan
- **Important**: Performance regression in build times
- **Suggested**: Further optimization opportunities
---
## Special Cases
### Technical Debt
- Acknowledge existing tech debt but don't require fixing in unrelated PR
- Exception: If change makes tech debt worse, it's Important to address
### Scope Creep
- Don't request changes outside PR scope
- Can note as "Future consideration" but not required for this PR
### Linter-Catchable Issues
- Don't flag issues that automated tools handle
- Exception: If linter is misconfigured and missing real issues
### Personal Preferences
- Don't flag unless grounded in project standards or architectural principles
- Use "I-statements" if suggesting alternative approaches
---
## Summary
**Critical**: Block merge, must fix (security, stability, architecture)
**Important**: Should fix before merge (testing, quality, performance)
**Suggested**: Nice to have, consider effort vs benefit
**Optional**: Acknowledge good practices, keep brief

View File

@@ -0,0 +1,161 @@
# Review Psychology: Constructive Feedback Phrasing
Effective code review feedback is clear, actionable, and constructive. This guide provides phrasing patterns for inline comments.
## Core Directives
- Ask questions for design decisions, be prescriptive for clear violations
- Focus on code, not people ("This code..." not "You...")
- Use I-statements for subjective feedback ("Hard for me to understand...")
- Explain rationale with every recommendation
- Avoid: "just", "simply", "obviously", "easy"
- Keep positive feedback brief (list only, no elaboration)
---
## Phrasing Templates
### Critical Issues (Prescriptive)
**Pattern**: State problem + Provide solution + Explain why
```
**[file:line]** - CRITICAL: [Issue description]
[Specific fix with code example if applicable]
[Rationale explaining why this is critical]
Reference: [docs link if applicable]
```
**Example**:
```
**data/vault/VaultRepository.kt:145** - CRITICAL: PIN stored without encryption
PIN must be encrypted using Android Keystore, not stored in plaintext SharedPreferences.
Plaintext storage exposes the PIN to backup systems and rooted devices.
Reference: docs/ARCHITECTURE.md#security
```
---
### Suggested Improvements (Exploratory)
**Pattern**: Observe + Suggest + Explain benefit
```
**[file:line]** - Consider [alternative approach]
[Current observation]
Can we [specific suggestion]?
[Benefit or rationale]
```
**Example**:
```
**app/login/LoginScreen.kt:89** - Consider using existing BitwardenButton
This custom button implementation looks similar to `ui/components/BitwardenButton.kt:45`.
Can we use the existing component to maintain consistency across the app?
```
---
### Questions (Collaborative)
**Pattern**: Ask + Provide context (optional)
```
**[file:line]** - [Question about intent or approach]?
[Optional context or observation]
```
**Example**:
```
**data/sync/SyncManager.kt:234** - How does this handle concurrent sync attempts?
It looks like multiple coroutines could call `startSync()` simultaneously.
Is there a mechanism to prevent race conditions, or is that handled elsewhere?
```
---
### Test Suggestions
**Pattern**: Observe gap + Suggest specific test + Provide skeleton
```
**[file:line]** - Consider adding test for [scenario]
[Rationale]
```kotlin
@Test
fun `test description`() = runTest {
// Test skeleton
}
```
```
**Example**:
```
**data/auth/BiometricRepository.kt** - Consider adding test for cancellation scenario
This would prevent regression of the bug you just fixed:
```kotlin
@Test
fun `when biometric cancelled then returns cancelled state`() = runTest {
coEvery { biometricPrompt.authenticate() } returns null
val result = repository.authenticate()
assertEquals(AuthResult.Cancelled, result)
}
```
```
---
## When to Be Prescriptive vs Ask Questions
**Be Prescriptive** (Tell them what to do):
- Security issues
- Architecture pattern violations
- Null safety problems
- Compilation errors
- Documented project standards
**Ask Questions** (Seek explanation):
- Design decisions with multiple valid approaches
- Performance trade-offs without data
- Unclear intent or reasoning
- Scope decisions (this PR vs future work)
- Patterns not documented in project guidelines
---
## Special Cases
**Nitpicks** - For truly minor suggestions, use "Nit:" prefix:
```
**Nit**: Extra blank line at line 145
```
**Uncertainty** - If unsure, acknowledge it:
```
I'm not certain, but this might be called frequently.
Has this been profiled?
```
**Positive Feedback** - Brief list only, no elaboration:
```
## Good Practices
- Proper Hilt DI usage throughout
- Comprehensive unit test coverage
- Clear separation of concerns
```

View File

@@ -0,0 +1,90 @@
# Security Patterns Quick Reference
Quick reference for Bitwarden Android security patterns during code reviews. For comprehensive details, read `docs/ARCHITECTURE.md#security`.
## Encryption and Key Storage
**✅ GOOD - Android Keystore**:
```kotlin
// Sensitive data encrypted with Keystore
class SecureStorage @Inject constructor(
private val keystoreManager: KeystoreManager
) {
suspend fun storePin(pin: String): Result<Unit> = runCatching {
val encrypted = keystoreManager.encrypt(pin.toByteArray())
securePreferences.putBytes(KEY_PIN, encrypted)
}
}
// Or use EncryptedSharedPreferences
val encryptedPrefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
```
**❌ BAD - Plaintext or weak encryption**:
```kotlin
// ❌ CRITICAL - Plaintext storage
sharedPreferences.edit {
putString("pin", userPin) // Never store sensitive data in plaintext
}
// ❌ CRITICAL - Weak encryption
val cipher = Cipher.getInstance("DES") // Use AES-256-GCM
// ❌ CRITICAL - Hardcoded keys
val key = "my_secret_key_123" // Use Android Keystore
```
**Key Rules**:
- Use Android Keystore for encryption keys
- Use EncryptedSharedPreferences for simple key-value storage
- Use AES-256-GCM for encryption
- Never store sensitive data in plaintext
- Never hardcode encryption keys
Reference: `docs/ARCHITECTURE.md#security`
---
## Logging Sensitive Data
**✅ GOOD - No sensitive data**:
```kotlin
Log.d(TAG, "Authentication attempt for user")
Log.d(TAG, "Vault sync completed with ${items.size} items")
```
**❌ BAD - Logs sensitive data**:
```kotlin
// ❌ CRITICAL
Log.d(TAG, "Password: $password")
Log.d(TAG, "Auth token: $token")
Log.d(TAG, "PIN: $pin")
Log.d(TAG, "Encryption key: ${key.encoded}")
```
**Key Rules**:
- Never log passwords, PINs, tokens, keys
- Never log encryption keys or sensitive data
- Be careful with error messages (don't include sensitive context)
---
## Quick Checklist
### Security
- [ ] Sensitive data encrypted with Keystore?
- [ ] No plaintext passwords/keys?
- [ ] No sensitive data in logs?
- [ ] Using AES-256-GCM for encryption?
- [ ] No hardcoded encryption keys?
---
For comprehensive security details, always refer to:
- `docs/ARCHITECTURE.md#security` - Complete security architecture and zero-knowledge principles

View File

@@ -0,0 +1,127 @@
# Testing Patterns Quick Reference
Quick reference for Bitwarden Android testing patterns during code reviews. For comprehensive details, read `docs/ARCHITECTURE.md` and `docs/STYLE_AND_BEST_PRACTICES.md`.
## ViewModel Tests
**✅ GOOD - Tests behavior**:
```kotlin
@Test
fun `when login succeeds then state updates to success`() = runTest {
// Arrange
val viewModel = LoginViewModel(mockRepository)
coEvery { mockRepository.login(any(), any()) } returns Result.success(User())
// Act
viewModel.onLoginClicked("user@example.com", "password")
// Assert
viewModel.state.test {
assertEquals(LoginState.Loading, awaitItem())
assertEquals(LoginState.Success, awaitItem())
}
}
```
**❌ BAD - Tests implementation**:
```kotlin
@Test
fun `repository is called with correct parameters`() {
// ❌ This tests implementation details, not behavior
viewModel.onLoginClicked("user", "pass")
coVerify { mockRepository.login("user", "pass") }
}
```
**Key Rules**:
- Test behavior, not implementation
- Use `runTest` for coroutine tests
- Use Turbine for Flow testing
- Use MockK for mocking
---
## Repository Tests
**✅ GOOD - Tests data transformations**:
```kotlin
@Test
fun `fetchItems maps API response to domain model`() = runTest {
// Arrange
val apiResponse = listOf(ApiItem(id = "1", name = "Test"))
coEvery { apiService.getItems() } returns apiResponse
// Act
val result = repository.fetchItems()
// Assert
assertTrue(result.isSuccess)
assertEquals(
listOf(DomainItem(id = "1", name = "Test")),
result.getOrThrow()
)
}
```
**Key Rules**:
- Test data transformations
- Test error handling (network failures, API errors)
- Test caching behavior if applicable
- Mock API services and databases
Reference: Project uses JUnit 5, MockK, Turbine, kotlinx-coroutines-test
---
## Null Safety
**✅ GOOD - Safe handling**:
```kotlin
// Safe call with elvis operator
val result = apiService.getData() ?: return State.Error("No data")
// Let with safe call
intent?.getStringExtra("key")?.let { value ->
processValue(value)
}
// Require with message
val data = requireNotNull(response.data) { "Response data must not be null" }
```
**❌ BAD - Unsafe assertions**:
```kotlin
// ❌ Unsafe - can crash
val result = apiService.getData()!!
// ❌ Platform type unchecked
val intent: Intent = getIntent() // Could be null from Java
val value = intent.getStringExtra("key") // Potential NPE
```
**Key Rules**:
- Avoid `!!` unless safety is guaranteed (rare)
- Handle platform types with explicit nullability
- Use safe calls (`?.`), elvis operator (`?:`), or explicit checks
- Use `requireNotNull` with descriptive message if crash is acceptable
---
## Quick Checklist
### Testing
- [ ] ViewModels have unit tests?
- [ ] Tests verify behavior, not implementation?
- [ ] Edge cases covered?
- [ ] Error scenarios tested?
### Code Quality
- [ ] Null safety handled properly (no `!!` without guarantee)?
- [ ] Public APIs have KDoc?
- [ ] Following naming conventions?
---
For comprehensive details, always refer to:
- `docs/ARCHITECTURE.md` - Full architecture patterns
- `docs/STYLE_AND_BEST_PRACTICES.md` - Complete style guide

View File

@@ -0,0 +1,85 @@
# Compose UI Patterns Quick Reference
Quick reference for Bitwarden Android Compose UI patterns during code reviews. For comprehensive details, read `docs/ARCHITECTURE.md` and `docs/STYLE_AND_BEST_PRACTICES.md`.
## Component Reuse
**✅ GOOD - Uses existing components**:
```kotlin
BitwardenButton(
text = "Submit",
onClick = onSubmit
)
BitwardenTextField(
value = text,
onValueChange = onTextChange,
label = "Email"
)
```
**❌ BAD - Duplicates existing components**:
```kotlin
// ❌ Recreating BitwardenButton
Button(
onClick = onSubmit,
colors = ButtonDefaults.buttonColors(
containerColor = BitwardenTheme.colorScheme.primary
)
) {
Text("Submit")
}
```
**Key Rules**:
- Check `:ui` module for existing components before creating custom ones
- Use BitwardenButton, BitwardenTextField, etc. for consistency
- Place new reusable components in `:ui` module
---
## Theme Usage
**✅ GOOD - Uses theme**:
```kotlin
Text(
text = "Title",
style = BitwardenTheme.typography.titleLarge,
color = BitwardenTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(16.dp)) // Standard spacing
```
**❌ BAD - Hardcoded values**:
```kotlin
Text(
text = "Title",
style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold), // Use theme
color = Color(0xFF0066FF) // Use theme color
)
Spacer(modifier = Modifier.height(17.dp)) // Non-standard spacing
```
**Key Rules**:
- Use `BitwardenTheme.colorScheme` for colors
- Use `BitwardenTheme.typography` for text styles
- Use standard spacing (4.dp, 8.dp, 16.dp, 24.dp)
---
## Quick Checklist
### UI Patterns
- [ ] Using existing Bitwarden components from `:ui` module?
- [ ] Using BitwardenTheme for colors and typography?
- [ ] Using standard spacing values (4, 8, 16, 24 dp)?
- [ ] No hardcoded colors or text styles?
- [ ] UI is stateless (observes state, doesn't modify)?
---
For comprehensive details, always refer to:
- `docs/ARCHITECTURE.md` - Full architecture patterns
- `docs/STYLE_AND_BEST_PRACTICES.md` - Complete style guide