一键导入
detekt-custom-rules
Custom detekt rules, DI/privacy/suppression guardrails, detekt.yml configuration, and false-positive triage.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Custom detekt rules, DI/privacy/suppression guardrails, detekt.yml configuration, and false-positive triage.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use this skill to generate well-branded interfaces and assets for RIPDPI (an Android-native, Compose-first VPN and DPI-bypass app), either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, brand-ship icons, and an interactive UI kit of every public composable from the live ui/components tree.
Use when managing the Rust workspace, adding/removing crates, editing workspace dependencies, running cargo nextest/audit/deny, configuring Cargo profiles for Android cross-compilation, debugging Cargo.lock churn, migrating crate edition, or wiring Gradle to cargo via the ripdpi.android.rust-native plugin.
Use when modifying the diagnostics scan pipeline, ScanRequest/ScanReport types, ProbeTask families, ripdpi-monitor-engine / ripdpi-diagnostics-* crates, strategy-probe candidates, the diagnostics catalog (packs/profiles), wire-schema contracts between Rust and Kotlin, DIAGNOSTICS_ENGINE_SCHEMA_VERSION, golden contract tests, or adding a new probe type / profile. Triggers on diagnostics scans, strategy probes, automatic audit, dpi-detector profiles, or anything in core/diagnostics or native/rust/crates/ripdpi-monitor-*.
Android-specific Rust build, verification, and packaging — per-target 16 KiB page alignment, size-optimized release profile, ELF symbol allowlist, .so size budgets, NDK 29 specifics. Use when modifying .cargo/config.toml for Android targets, the workspace [profile.release] / [profile.android-jni] block, or when verifying a built .so before release.
Telemetry and observability discipline for the RIPDPI Android Rust stack — control-plane vs data-plane logging, bounded flume event queues, atomic counters, snapshot polling, readiness callbacks, and deterministic JSON contracts. Use when authoring or modifying telemetry emission code, the bounded event ring, the Kotlin-side telemetry consumer, or any per-packet logging.
Edge-to-edge and window-inset handling for Compose screens, scaffolds, sheets, and system bars.
| name | detekt-custom-rules |
| description | Custom detekt rules, DI/privacy/suppression guardrails, detekt.yml configuration, and false-positive triage. |
Custom rule set diGuardrails in module :quality:detekt-rules. Enforces DI, Hilt, suppression, and resolver-log privacy conventions at compile time.
| 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/YourRuleNameTest.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