RIPDPI's own Compose conventions: ViewModel pattern, Route/Screen split, DataStore->StateFlow, RipDpiThemeTokens. Use for how this app does Compose, not generic Compose API questions (see compose).
日本語の概要は準備中です。原文の説明を表示しています。
Register, activate, and test a custom detekt rule in :quality:detekt-rules. Use when adding a DI/privacy/suppression guardrail rule or debugging a false positive.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Custom rule set diGuardrails in module :quality:detekt-rules. Enforces DI, Hilt, suppression, and resolver-log privacy conventions at compile time.
scripts/ci/check_harness_manifests.py (validate_factual_ground_truth) mechanically checks that every rule registered in RipDpiRuleSetProvider.kt appears in this table -- omitting a rule here fails CI rather than silently drifting.
| Rule | What It Catches | Why |
|---|---|---|
InjectConstructorDefaultParameter | Default values on @Inject constructor params | Hilt ignores defaults, causing silent behavior mismatch |
HiltViewModelApplicationContext | @ApplicationContext in @HiltViewModel constructors | Forces narrower abstractions, improves testability |
DisallowNewSuppression | @Suppress without an adjacent ROADMAP-architecture-refactor allowlist comment | Prevents new lint debt from becoming invisible |
NoResolverIpInLogs | Resolver or upstream IP identifiers interpolated into log calls | Keeps user-visible network state out of logs |
quality/detekt-rules/
src/main/kotlin/com/poyka/ripdpi/quality/detekt/
RipDpiRuleSetProvider.kt -- Registers all rules (id: "diGuardrails")
AnnotationMatchers.kt -- Shared helpers: hasAnnotation(), findAnnotation()
InjectConstructorDefaultParameter.kt
HiltViewModelApplicationContext.kt
DisallowNewSuppression.kt
NoResolverIpInLogs.kt
src/test/kotlin/com/poyka/ripdpi/quality/detekt/
InjectConstructorDefaultParameterTest.kt
HiltViewModelApplicationContextTest.kt
DisallowNewSuppressionTest.kt
NoResolverIpInLogsTest.kt
Create src/main/kotlin/com/poyka/ripdpi/quality/detekt/<YourRuleName>.kt:
package com.poyka.ripdpi.quality.detekt
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import org.jetbrains.kotlin.psi.KtClass
class YourRuleName(config: Config) : Rule(config) {
override val issue: Issue =
Issue(
id = "YourRuleName",
severity = Severity.Defect,
description = "One-line description of what this catches.",
debt = Debt.FIVE_MINS,
)
override fun visitClass(klass: KtClass) {
super.visitClass(klass)
// Use hasAnnotation() from AnnotationMatchers.kt
if (!klass.hasAnnotation("SomeAnnotation")) return
// Check condition and report
report(
CodeSmell(
issue = issue,
entity = Entity.from(klass),
message = "Actionable message: what to do instead.",
),
)
}
}
Add to RipDpiRuleSetProvider.kt:
override fun instance(config: Config): RuleSet =
RuleSet(
ruleSetId,
listOf(
InjectConstructorDefaultParameter(config),
HiltViewModelApplicationContext(config),
DisallowNewSuppression(config),
NoResolverIpInLogs(config),
YourRuleName(config), // <-- add here
),
)
Add to config/detekt/detekt.yml:
diGuardrails:
active: true
InjectConstructorDefaultParameter:
active: true
HiltViewModelApplicationContext:
active: true
NoResolverIpInLogs:
active: true
YourRuleName: # <-- add here
active: true
DisallowNewSuppression is registered in RipDpiRuleSetProvider and inherits the active diGuardrails rule-set setting; it has no per-rule override in the current config.
Create src/test/kotlin/com/poyka/ripdpi/quality/detekt/<YourRuleName>Test.kt:
package com.poyka.ripdpi.quality.detekt
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class YourRuleNameTest {
private val rule = YourRuleName(Config.empty)
@Test
fun `reports violation on annotated class`() {
val code = """
@SomeAnnotation
class Bad { /* violating pattern */ }
""".trimIndent()
val findings = rule.compileAndLint(code)
assertEquals(1, findings.size)
}
@Test
fun `allows non-annotated class`() {
val code = """
class Fine { /* same pattern but no annotation */ }
""".trimIndent()
val findings = rule.compileAndLint(code)
assertEquals(0, findings.size)
}
}
Cover: positive detection, negative (no false positive), edge cases (primary vs secondary constructors, nested classes).
Use the shared helpers from AnnotationMatchers.kt:
// Check if a KtAnnotated element has an annotation by simple name
klass.hasAnnotation("HiltViewModel") // matches @HiltViewModel
constructor.hasAnnotation("Inject") // matches @Inject
// Find the annotation entry (for Entity.from targeting)
parameter.findAnnotation("ApplicationContext") // returns KtAnnotationEntry?
Important: These match by simple name, not fully-qualified name. hasAnnotation("Inject") matches both @javax.inject.Inject and @com.google.inject.Inject.
| Method | When To Use |
|---|---|
visitClass(KtClass) | Checking class-level patterns |
visitPrimaryConstructor(KtPrimaryConstructor) | Constructor parameter rules |
visitSecondaryConstructor(KtSecondaryConstructor) | Must handle alongside primary |
visitNamedFunction(KtNamedFunction) | Function-level rules |
visitProperty(KtProperty) | Property declaration rules |
Use getStrictParentOfType<KtClassOrObject>() to navigate from a constructor to its owning class.
ripdpi.android.detekt.gradle.kts configures detekt for all modules:
config/detekt/detekt.yml**/build/**, **/generated/**, **/jni/**, **/cpp/**detekt-baseline.xml if present in module:quality:detekt-rules) + detekt-compose-rulesRun detekt:
./gradlew staticAnalysis # detekt + ktlint + lint
./gradlew detektDebug # detekt only (single variant)
| Mistake | Fix |
|---|---|
Forgetting to register in RipDpiRuleSetProvider | Rule exists but never runs. Add to the listOf(...) in instance(). |
| Using FQN for annotation matching | hasAnnotation("javax.inject.Inject") won't match. Use simple name: "Inject". |
Not activating in detekt.yml | Rule is registered but inactive. Add YourRuleName: { active: true } under diGuardrails:. |
| Testing only primary constructors | Many rules apply to both primary and secondary. Override both visitor methods. |
| Reporting on wrong entity | Use Entity.from(specificElement) not Entity.from(constructor) -- highlights the exact problem location. |
Missing super.visitX() call | Always call super.visitPrimaryConstructor(constructor) etc. to continue traversal. |
config/detekt/detekt.yml -- Full detekt configurationbuild-logic/convention/src/main/kotlin/ripdpi.android.detekt.gradle.kts -- Convention pluginまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
RIPDPI's own Compose conventions: ViewModel pattern, Route/Screen split, DataStore->StateFlow, RipDpiThemeTokens. Use for how this app does Compose, not generic Compose API questions (see compose).
日本語の概要は準備中です。原文の説明を表示しています。
ADB-based RIPDPI device/emulator debugging: build/install/launch, logcat filtering, fixture port-forwarding, instrumented tests, crash/ANR triage. Use when debugging on a real device or emulator.
日本語の概要は準備中です。原文の説明を表示しています。
RIPDPI Appium launch-contract reference: start routes and permission/service/data presets. Use when a test launches to the wrong screen or a new automation route is added. General flakiness: appium-test-debug.
日本語の概要は準備中です。原文の説明を表示しています。
RIPDPI Appium test authoring: page objects, resource-id locators, assertions, wait tiers. Use when writing a new Appium test or page object, or adding coverage for a new screen.
日本語の概要は準備中です。原文の説明を表示しています。
RIPDPI Appium failure triage: flaky tests, locator/session/wait issues, screenshot and element-tree debugging. Use when an Appium test fails or is flaky.
日本語の概要は準備中です。原文の説明を表示しています。
Add or modify a GitHub Actions job. Use when editing a file under .github/workflows/. Not for running a workflow locally (local-ci-act) or release signing (release-signing).
日本語の概要は準備中です。原文の説明を表示しています。